github.com/emate/nomad@v0.8.2-wo-binpacking/nomad/state/state_store.go (about) 1 package state 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "log" 8 "sort" 9 "time" 10 11 "github.com/hashicorp/go-memdb" 12 multierror "github.com/hashicorp/go-multierror" 13 "github.com/hashicorp/nomad/helper" 14 "github.com/hashicorp/nomad/nomad/structs" 15 ) 16 17 // IndexEntry is used with the "index" table 18 // for managing the latest Raft index affecting a table. 19 type IndexEntry struct { 20 Key string 21 Value uint64 22 } 23 24 // StateStoreConfig is used to configure a new state store 25 type StateStoreConfig struct { 26 // LogOutput is used to configure the output of the state store's logs 27 LogOutput io.Writer 28 29 // Region is the region of the server embedding the state store. 30 Region string 31 } 32 33 // The StateStore is responsible for maintaining all the Nomad 34 // state. It is manipulated by the FSM which maintains consistency 35 // through the use of Raft. The goals of the StateStore are to provide 36 // high concurrency for read operations without blocking writes, and 37 // to provide write availability in the face of reads. EVERY object 38 // returned as a result of a read against the state store should be 39 // considered a constant and NEVER modified in place. 40 type StateStore struct { 41 logger *log.Logger 42 db *memdb.MemDB 43 44 // config is the passed in configuration 45 config *StateStoreConfig 46 47 // abandonCh is used to signal watchers that this state store has been 48 // abandoned (usually during a restore). This is only ever closed. 49 abandonCh chan struct{} 50 } 51 52 // NewStateStore is used to create a new state store 53 func NewStateStore(config *StateStoreConfig) (*StateStore, error) { 54 // Create the MemDB 55 db, err := memdb.NewMemDB(stateStoreSchema()) 56 if err != nil { 57 return nil, fmt.Errorf("state store setup failed: %v", err) 58 } 59 60 // Create the state store 61 s := &StateStore{ 62 logger: log.New(config.LogOutput, "", log.LstdFlags), 63 db: db, 64 config: config, 65 abandonCh: make(chan struct{}), 66 } 67 return s, nil 68 } 69 70 // Config returns the state store configuration. 71 func (s *StateStore) Config() *StateStoreConfig { 72 return s.config 73 } 74 75 // Snapshot is used to create a point in time snapshot. Because 76 // we use MemDB, we just need to snapshot the state of the underlying 77 // database. 78 func (s *StateStore) Snapshot() (*StateSnapshot, error) { 79 snap := &StateSnapshot{ 80 StateStore: StateStore{ 81 logger: s.logger, 82 config: s.config, 83 db: s.db.Snapshot(), 84 }, 85 } 86 return snap, nil 87 } 88 89 // Restore is used to optimize the efficiency of rebuilding 90 // state by minimizing the number of transactions and checking 91 // overhead. 92 func (s *StateStore) Restore() (*StateRestore, error) { 93 txn := s.db.Txn(true) 94 r := &StateRestore{ 95 txn: txn, 96 } 97 return r, nil 98 } 99 100 // AbandonCh returns a channel you can wait on to know if the state store was 101 // abandoned. 102 func (s *StateStore) AbandonCh() <-chan struct{} { 103 return s.abandonCh 104 } 105 106 // Abandon is used to signal that the given state store has been abandoned. 107 // Calling this more than one time will panic. 108 func (s *StateStore) Abandon() { 109 close(s.abandonCh) 110 } 111 112 // QueryFn is the definition of a function that can be used to implement a basic 113 // blocking query against the state store. 114 type QueryFn func(memdb.WatchSet, *StateStore) (resp interface{}, index uint64, err error) 115 116 // BlockingQuery takes a query function and runs the function until the minimum 117 // query index is met or until the passed context is cancelled. 118 func (s *StateStore) BlockingQuery(query QueryFn, minIndex uint64, ctx context.Context) ( 119 resp interface{}, index uint64, err error) { 120 121 RUN_QUERY: 122 // We capture the state store and its abandon channel but pass a snapshot to 123 // the blocking query function. We operate on the snapshot to allow separate 124 // calls to the state store not all wrapped within the same transaction. 125 abandonCh := s.AbandonCh() 126 snap, _ := s.Snapshot() 127 stateSnap := &snap.StateStore 128 129 // We can skip all watch tracking if this isn't a blocking query. 130 var ws memdb.WatchSet 131 if minIndex > 0 { 132 ws = memdb.NewWatchSet() 133 134 // This channel will be closed if a snapshot is restored and the 135 // whole state store is abandoned. 136 ws.Add(abandonCh) 137 } 138 139 resp, index, err = query(ws, stateSnap) 140 if err != nil { 141 return nil, index, err 142 } 143 144 // We haven't reached the min-index yet. 145 if minIndex > 0 && index <= minIndex { 146 if err := ws.WatchCtx(ctx); err != nil { 147 return nil, index, err 148 } 149 150 goto RUN_QUERY 151 } 152 153 return resp, index, nil 154 } 155 156 // UpsertPlanResults is used to upsert the results of a plan. 157 func (s *StateStore) UpsertPlanResults(index uint64, results *structs.ApplyPlanResultsRequest) error { 158 txn := s.db.Txn(true) 159 defer txn.Abort() 160 161 // Upsert the newly created or updated deployment 162 if results.Deployment != nil { 163 if err := s.upsertDeploymentImpl(index, results.Deployment, txn); err != nil { 164 return err 165 } 166 } 167 168 // Update the status of deployments effected by the plan. 169 if len(results.DeploymentUpdates) != 0 { 170 s.upsertDeploymentUpdates(index, results.DeploymentUpdates, txn) 171 } 172 173 // Attach the job to all the allocations. It is pulled out in the payload to 174 // avoid the redundancy of encoding, but should be denormalized prior to 175 // being inserted into MemDB. 176 structs.DenormalizeAllocationJobs(results.Job, results.Alloc) 177 178 // Calculate the total resources of allocations. It is pulled out in the 179 // payload to avoid encoding something that can be computed, but should be 180 // denormalized prior to being inserted into MemDB. 181 for _, alloc := range results.Alloc { 182 if alloc.Resources != nil { 183 continue 184 } 185 186 alloc.Resources = new(structs.Resources) 187 for _, task := range alloc.TaskResources { 188 alloc.Resources.Add(task) 189 } 190 191 // Add the shared resources 192 alloc.Resources.Add(alloc.SharedResources) 193 } 194 195 // Upsert the allocations 196 if err := s.upsertAllocsImpl(index, results.Alloc, txn); err != nil { 197 return err 198 } 199 200 // COMPAT: Nomad versions before 0.7.1 did not include the eval ID when 201 // applying the plan. Thus while we are upgrading, we ignore updating the 202 // modify index of evaluations from older plans. 203 if results.EvalID != "" { 204 // Update the modify index of the eval id 205 if err := s.updateEvalModifyIndex(txn, index, results.EvalID); err != nil { 206 return err 207 } 208 } 209 210 txn.Commit() 211 return nil 212 } 213 214 // upsertDeploymentUpdates updates the deployments given the passed status 215 // updates. 216 func (s *StateStore) upsertDeploymentUpdates(index uint64, updates []*structs.DeploymentStatusUpdate, txn *memdb.Txn) error { 217 for _, u := range updates { 218 if err := s.updateDeploymentStatusImpl(index, u, txn); err != nil { 219 return err 220 } 221 } 222 223 return nil 224 } 225 226 // UpsertJobSummary upserts a job summary into the state store. 227 func (s *StateStore) UpsertJobSummary(index uint64, jobSummary *structs.JobSummary) error { 228 txn := s.db.Txn(true) 229 defer txn.Abort() 230 231 // COMPAT 0.7: Upgrade old objects that do not have namespaces 232 if jobSummary.Namespace == "" { 233 jobSummary.Namespace = structs.DefaultNamespace 234 } 235 236 // Check if the job summary already exists 237 existing, err := txn.First("job_summary", "id", jobSummary.Namespace, jobSummary.JobID) 238 if err != nil { 239 return fmt.Errorf("job summary lookup failed: %v", err) 240 } 241 242 // Setup the indexes correctly 243 if existing != nil { 244 jobSummary.CreateIndex = existing.(*structs.JobSummary).CreateIndex 245 jobSummary.ModifyIndex = index 246 } else { 247 jobSummary.CreateIndex = index 248 jobSummary.ModifyIndex = index 249 } 250 251 // Update the index 252 if err := txn.Insert("job_summary", jobSummary); err != nil { 253 return err 254 } 255 256 // Update the indexes table for job summary 257 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 258 return fmt.Errorf("index update failed: %v", err) 259 } 260 261 txn.Commit() 262 return nil 263 } 264 265 // DeleteJobSummary deletes the job summary with the given ID. This is for 266 // testing purposes only. 267 func (s *StateStore) DeleteJobSummary(index uint64, namespace, id string) error { 268 txn := s.db.Txn(true) 269 defer txn.Abort() 270 271 // COMPAT 0.7: Upgrade old objects that do not have namespaces 272 if namespace == "" { 273 namespace = structs.DefaultNamespace 274 } 275 276 // Delete the job summary 277 if _, err := txn.DeleteAll("job_summary", "id", namespace, id); err != nil { 278 return fmt.Errorf("deleting job summary failed: %v", err) 279 } 280 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 281 return fmt.Errorf("index update failed: %v", err) 282 } 283 txn.Commit() 284 return nil 285 } 286 287 // UpsertDeployment is used to insert a new deployment. If cancelPrior is set to 288 // true, all prior deployments for the same job will be cancelled. 289 func (s *StateStore) UpsertDeployment(index uint64, deployment *structs.Deployment) error { 290 txn := s.db.Txn(true) 291 defer txn.Abort() 292 if err := s.upsertDeploymentImpl(index, deployment, txn); err != nil { 293 return err 294 } 295 txn.Commit() 296 return nil 297 } 298 299 func (s *StateStore) upsertDeploymentImpl(index uint64, deployment *structs.Deployment, txn *memdb.Txn) error { 300 // Check if the deployment already exists 301 existing, err := txn.First("deployment", "id", deployment.ID) 302 if err != nil { 303 return fmt.Errorf("deployment lookup failed: %v", err) 304 } 305 306 // COMPAT 0.7: Upgrade old objects that do not have namespaces 307 if deployment.Namespace == "" { 308 deployment.Namespace = structs.DefaultNamespace 309 } 310 311 // Setup the indexes correctly 312 if existing != nil { 313 deployment.CreateIndex = existing.(*structs.Deployment).CreateIndex 314 deployment.ModifyIndex = index 315 } else { 316 deployment.CreateIndex = index 317 deployment.ModifyIndex = index 318 } 319 320 // Insert the deployment 321 if err := txn.Insert("deployment", deployment); err != nil { 322 return err 323 } 324 325 // Update the indexes table for deployment 326 if err := txn.Insert("index", &IndexEntry{"deployment", index}); err != nil { 327 return fmt.Errorf("index update failed: %v", err) 328 } 329 330 // If the deployment is being marked as complete, set the job to stable. 331 if deployment.Status == structs.DeploymentStatusSuccessful { 332 if err := s.updateJobStabilityImpl(index, deployment.Namespace, deployment.JobID, deployment.JobVersion, true, txn); err != nil { 333 return fmt.Errorf("failed to update job stability: %v", err) 334 } 335 } 336 337 return nil 338 } 339 340 func (s *StateStore) Deployments(ws memdb.WatchSet) (memdb.ResultIterator, error) { 341 txn := s.db.Txn(false) 342 343 // Walk the entire deployments table 344 iter, err := txn.Get("deployment", "id") 345 if err != nil { 346 return nil, err 347 } 348 349 ws.Add(iter.WatchCh()) 350 return iter, nil 351 } 352 353 func (s *StateStore) DeploymentsByNamespace(ws memdb.WatchSet, namespace string) (memdb.ResultIterator, error) { 354 txn := s.db.Txn(false) 355 356 // Walk the entire deployments table 357 iter, err := txn.Get("deployment", "namespace", namespace) 358 if err != nil { 359 return nil, err 360 } 361 362 ws.Add(iter.WatchCh()) 363 return iter, nil 364 } 365 366 func (s *StateStore) DeploymentsByIDPrefix(ws memdb.WatchSet, namespace, deploymentID string) (memdb.ResultIterator, error) { 367 txn := s.db.Txn(false) 368 369 // Walk the entire deployments table 370 iter, err := txn.Get("deployment", "id_prefix", deploymentID) 371 if err != nil { 372 return nil, err 373 } 374 375 ws.Add(iter.WatchCh()) 376 377 // Wrap the iterator in a filter 378 wrap := memdb.NewFilterIterator(iter, deploymentNamespaceFilter(namespace)) 379 return wrap, nil 380 } 381 382 // deploymentNamespaceFilter returns a filter function that filters all 383 // deployment not in the given namespace. 384 func deploymentNamespaceFilter(namespace string) func(interface{}) bool { 385 return func(raw interface{}) bool { 386 d, ok := raw.(*structs.Deployment) 387 if !ok { 388 return true 389 } 390 391 return d.Namespace != namespace 392 } 393 } 394 395 func (s *StateStore) DeploymentByID(ws memdb.WatchSet, deploymentID string) (*structs.Deployment, error) { 396 txn := s.db.Txn(false) 397 return s.deploymentByIDImpl(ws, deploymentID, txn) 398 } 399 400 func (s *StateStore) deploymentByIDImpl(ws memdb.WatchSet, deploymentID string, txn *memdb.Txn) (*structs.Deployment, error) { 401 watchCh, existing, err := txn.FirstWatch("deployment", "id", deploymentID) 402 if err != nil { 403 return nil, fmt.Errorf("deployment lookup failed: %v", err) 404 } 405 ws.Add(watchCh) 406 407 if existing != nil { 408 return existing.(*structs.Deployment), nil 409 } 410 411 return nil, nil 412 } 413 414 func (s *StateStore) DeploymentsByJobID(ws memdb.WatchSet, namespace, jobID string) ([]*structs.Deployment, error) { 415 txn := s.db.Txn(false) 416 417 // COMPAT 0.7: Upgrade old objects that do not have namespaces 418 if namespace == "" { 419 namespace = structs.DefaultNamespace 420 } 421 422 // Get an iterator over the deployments 423 iter, err := txn.Get("deployment", "job", namespace, jobID) 424 if err != nil { 425 return nil, err 426 } 427 428 ws.Add(iter.WatchCh()) 429 430 var out []*structs.Deployment 431 for { 432 raw := iter.Next() 433 if raw == nil { 434 break 435 } 436 437 d := raw.(*structs.Deployment) 438 out = append(out, d) 439 } 440 441 return out, nil 442 } 443 444 // LatestDeploymentByJobID returns the latest deployment for the given job. The 445 // latest is determined strictly by CreateIndex. 446 func (s *StateStore) LatestDeploymentByJobID(ws memdb.WatchSet, namespace, jobID string) (*structs.Deployment, error) { 447 txn := s.db.Txn(false) 448 449 // COMPAT 0.7: Upgrade old objects that do not have namespaces 450 if namespace == "" { 451 namespace = structs.DefaultNamespace 452 } 453 454 // Get an iterator over the deployments 455 iter, err := txn.Get("deployment", "job", namespace, jobID) 456 if err != nil { 457 return nil, err 458 } 459 460 ws.Add(iter.WatchCh()) 461 462 var out *structs.Deployment 463 for { 464 raw := iter.Next() 465 if raw == nil { 466 break 467 } 468 469 d := raw.(*structs.Deployment) 470 if out == nil || out.CreateIndex < d.CreateIndex { 471 out = d 472 } 473 } 474 475 return out, nil 476 } 477 478 // DeleteDeployment is used to delete a set of deployments by ID 479 func (s *StateStore) DeleteDeployment(index uint64, deploymentIDs []string) error { 480 txn := s.db.Txn(true) 481 defer txn.Abort() 482 483 if len(deploymentIDs) == 0 { 484 return nil 485 } 486 487 for _, deploymentID := range deploymentIDs { 488 // Lookup the deployment 489 existing, err := txn.First("deployment", "id", deploymentID) 490 if err != nil { 491 return fmt.Errorf("deployment lookup failed: %v", err) 492 } 493 if existing == nil { 494 return fmt.Errorf("deployment not found") 495 } 496 497 // Delete the deployment 498 if err := txn.Delete("deployment", existing); err != nil { 499 return fmt.Errorf("deployment delete failed: %v", err) 500 } 501 } 502 503 if err := txn.Insert("index", &IndexEntry{"deployment", index}); err != nil { 504 return fmt.Errorf("index update failed: %v", err) 505 } 506 507 txn.Commit() 508 return nil 509 } 510 511 // UpsertNode is used to register a node or update a node definition 512 // This is assumed to be triggered by the client, so we retain the value 513 // of drain/eligibility which is set by the scheduler. 514 func (s *StateStore) UpsertNode(index uint64, node *structs.Node) error { 515 txn := s.db.Txn(true) 516 defer txn.Abort() 517 518 // Check if the node already exists 519 existing, err := txn.First("nodes", "id", node.ID) 520 if err != nil { 521 return fmt.Errorf("node lookup failed: %v", err) 522 } 523 524 // Setup the indexes correctly 525 if existing != nil { 526 exist := existing.(*structs.Node) 527 node.CreateIndex = exist.CreateIndex 528 node.ModifyIndex = index 529 530 // Retain node events that have already been set on the node 531 node.Events = exist.Events 532 533 node.Drain = exist.Drain // Retain the drain mode 534 node.SchedulingEligibility = exist.SchedulingEligibility // Retain the eligibility 535 node.DrainStrategy = exist.DrainStrategy // Retain the drain strategy 536 } else { 537 // Because this is the first time the node is being registered, we should 538 // also create a node registration event 539 nodeEvent := &structs.NodeEvent{ 540 Message: "Node Registered", 541 Subsystem: "Cluster", 542 Timestamp: time.Unix(node.StatusUpdatedAt, 0), 543 } 544 node.Events = []*structs.NodeEvent{nodeEvent} 545 node.CreateIndex = index 546 node.ModifyIndex = index 547 } 548 549 // Insert the node 550 if err := txn.Insert("nodes", node); err != nil { 551 return fmt.Errorf("node insert failed: %v", err) 552 } 553 if err := txn.Insert("index", &IndexEntry{"nodes", index}); err != nil { 554 return fmt.Errorf("index update failed: %v", err) 555 } 556 557 txn.Commit() 558 return nil 559 } 560 561 // DeleteNode is used to deregister a node 562 func (s *StateStore) DeleteNode(index uint64, nodeID string) error { 563 txn := s.db.Txn(true) 564 defer txn.Abort() 565 566 // Lookup the node 567 existing, err := txn.First("nodes", "id", nodeID) 568 if err != nil { 569 return fmt.Errorf("node lookup failed: %v", err) 570 } 571 if existing == nil { 572 return fmt.Errorf("node not found") 573 } 574 575 // Delete the node 576 if err := txn.Delete("nodes", existing); err != nil { 577 return fmt.Errorf("node delete failed: %v", err) 578 } 579 if err := txn.Insert("index", &IndexEntry{"nodes", index}); err != nil { 580 return fmt.Errorf("index update failed: %v", err) 581 } 582 583 txn.Commit() 584 return nil 585 } 586 587 // UpdateNodeStatus is used to update the status of a node 588 func (s *StateStore) UpdateNodeStatus(index uint64, nodeID, status string) error { 589 txn := s.db.Txn(true) 590 defer txn.Abort() 591 592 // Lookup the node 593 existing, err := txn.First("nodes", "id", nodeID) 594 if err != nil { 595 return fmt.Errorf("node lookup failed: %v", err) 596 } 597 if existing == nil { 598 return fmt.Errorf("node not found") 599 } 600 601 // Copy the existing node 602 existingNode := existing.(*structs.Node) 603 copyNode := existingNode.Copy() 604 605 // Update the status in the copy 606 copyNode.Status = status 607 copyNode.ModifyIndex = index 608 609 // Insert the node 610 if err := txn.Insert("nodes", copyNode); err != nil { 611 return fmt.Errorf("node update failed: %v", err) 612 } 613 if err := txn.Insert("index", &IndexEntry{"nodes", index}); err != nil { 614 return fmt.Errorf("index update failed: %v", err) 615 } 616 617 txn.Commit() 618 return nil 619 } 620 621 // BatchUpdateNodeDrain is used to update the drain of a node set of nodes 622 func (s *StateStore) BatchUpdateNodeDrain(index uint64, updates map[string]*structs.DrainUpdate) error { 623 txn := s.db.Txn(true) 624 defer txn.Abort() 625 for node, update := range updates { 626 if err := s.updateNodeDrainImpl(txn, index, node, update.DrainStrategy, update.MarkEligible); err != nil { 627 return err 628 } 629 } 630 txn.Commit() 631 return nil 632 } 633 634 // UpdateNodeDrain is used to update the drain of a node 635 func (s *StateStore) UpdateNodeDrain(index uint64, nodeID string, 636 drain *structs.DrainStrategy, markEligible bool) error { 637 638 txn := s.db.Txn(true) 639 defer txn.Abort() 640 if err := s.updateNodeDrainImpl(txn, index, nodeID, drain, markEligible); err != nil { 641 return err 642 } 643 txn.Commit() 644 return nil 645 } 646 647 func (s *StateStore) updateNodeDrainImpl(txn *memdb.Txn, index uint64, nodeID string, 648 drain *structs.DrainStrategy, markEligible bool) error { 649 650 // Lookup the node 651 existing, err := txn.First("nodes", "id", nodeID) 652 if err != nil { 653 return fmt.Errorf("node lookup failed: %v", err) 654 } 655 if existing == nil { 656 return fmt.Errorf("node not found") 657 } 658 659 // Copy the existing node 660 existingNode := existing.(*structs.Node) 661 copyNode := existingNode.Copy() 662 663 // Update the drain in the copy 664 copyNode.Drain = drain != nil // COMPAT: Remove in Nomad 0.9 665 copyNode.DrainStrategy = drain 666 if drain != nil { 667 copyNode.SchedulingEligibility = structs.NodeSchedulingIneligible 668 } else if markEligible { 669 copyNode.SchedulingEligibility = structs.NodeSchedulingEligible 670 } 671 672 copyNode.ModifyIndex = index 673 674 // Insert the node 675 if err := txn.Insert("nodes", copyNode); err != nil { 676 return fmt.Errorf("node update failed: %v", err) 677 } 678 if err := txn.Insert("index", &IndexEntry{"nodes", index}); err != nil { 679 return fmt.Errorf("index update failed: %v", err) 680 } 681 682 return nil 683 } 684 685 // UpdateNodeEligibility is used to update the scheduling eligibility of a node 686 func (s *StateStore) UpdateNodeEligibility(index uint64, nodeID string, eligibility string) error { 687 688 txn := s.db.Txn(true) 689 defer txn.Abort() 690 691 // Lookup the node 692 existing, err := txn.First("nodes", "id", nodeID) 693 if err != nil { 694 return fmt.Errorf("node lookup failed: %v", err) 695 } 696 if existing == nil { 697 return fmt.Errorf("node not found") 698 } 699 700 // Copy the existing node 701 existingNode := existing.(*structs.Node) 702 copyNode := existingNode.Copy() 703 704 // Check if this is a valid action 705 if copyNode.DrainStrategy != nil && eligibility == structs.NodeSchedulingEligible { 706 return fmt.Errorf("can not set node's scheduling eligibility to eligible while it is draining") 707 } 708 709 // Update the eligibility in the copy 710 copyNode.SchedulingEligibility = eligibility 711 copyNode.ModifyIndex = index 712 713 // Insert the node 714 if err := txn.Insert("nodes", copyNode); err != nil { 715 return fmt.Errorf("node update failed: %v", err) 716 } 717 if err := txn.Insert("index", &IndexEntry{"nodes", index}); err != nil { 718 return fmt.Errorf("index update failed: %v", err) 719 } 720 721 txn.Commit() 722 return nil 723 } 724 725 // UpsertNodeEvents adds the node events to the nodes, rotating events as 726 // necessary. 727 func (s *StateStore) UpsertNodeEvents(index uint64, nodeEvents map[string][]*structs.NodeEvent) error { 728 txn := s.db.Txn(true) 729 defer txn.Abort() 730 731 for nodeID, events := range nodeEvents { 732 if err := s.upsertNodeEvents(index, nodeID, events, txn); err != nil { 733 return err 734 } 735 } 736 737 txn.Commit() 738 return nil 739 } 740 741 // upsertNodeEvent upserts a node event for a respective node. It also maintains 742 // that a fixed number of node events are ever stored simultaneously, deleting 743 // older events once this bound has been reached. 744 func (s *StateStore) upsertNodeEvents(index uint64, nodeID string, events []*structs.NodeEvent, txn *memdb.Txn) error { 745 // Lookup the node 746 existing, err := txn.First("nodes", "id", nodeID) 747 if err != nil { 748 return fmt.Errorf("node lookup failed: %v", err) 749 } 750 if existing == nil { 751 return fmt.Errorf("node not found") 752 } 753 754 // Copy the existing node 755 existingNode := existing.(*structs.Node) 756 copyNode := existingNode.Copy() 757 758 // Add the events, updating the indexes 759 for _, e := range events { 760 e.CreateIndex = index 761 copyNode.Events = append(copyNode.Events, e) 762 } 763 764 // Keep node events pruned to not exceed the max allowed 765 if l := len(copyNode.Events); l > structs.MaxRetainedNodeEvents { 766 delta := l - structs.MaxRetainedNodeEvents 767 copyNode.Events = copyNode.Events[delta:] 768 } 769 770 // Insert the node 771 if err := txn.Insert("nodes", copyNode); err != nil { 772 return fmt.Errorf("node update failed: %v", err) 773 } 774 if err := txn.Insert("index", &IndexEntry{"nodes", index}); err != nil { 775 return fmt.Errorf("index update failed: %v", err) 776 } 777 778 return nil 779 } 780 781 // NodeByID is used to lookup a node by ID 782 func (s *StateStore) NodeByID(ws memdb.WatchSet, nodeID string) (*structs.Node, error) { 783 txn := s.db.Txn(false) 784 785 watchCh, existing, err := txn.FirstWatch("nodes", "id", nodeID) 786 if err != nil { 787 return nil, fmt.Errorf("node lookup failed: %v", err) 788 } 789 ws.Add(watchCh) 790 791 if existing != nil { 792 return existing.(*structs.Node), nil 793 } 794 return nil, nil 795 } 796 797 // NodesByIDPrefix is used to lookup nodes by prefix 798 func (s *StateStore) NodesByIDPrefix(ws memdb.WatchSet, nodeID string) (memdb.ResultIterator, error) { 799 txn := s.db.Txn(false) 800 801 iter, err := txn.Get("nodes", "id_prefix", nodeID) 802 if err != nil { 803 return nil, fmt.Errorf("node lookup failed: %v", err) 804 } 805 ws.Add(iter.WatchCh()) 806 807 return iter, nil 808 } 809 810 // NodeBySecretID is used to lookup a node by SecretID 811 func (s *StateStore) NodeBySecretID(ws memdb.WatchSet, secretID string) (*structs.Node, error) { 812 txn := s.db.Txn(false) 813 814 watchCh, existing, err := txn.FirstWatch("nodes", "secret_id", secretID) 815 if err != nil { 816 return nil, fmt.Errorf("node lookup by SecretID failed: %v", err) 817 } 818 ws.Add(watchCh) 819 820 if existing != nil { 821 return existing.(*structs.Node), nil 822 } 823 return nil, nil 824 } 825 826 // Nodes returns an iterator over all the nodes 827 func (s *StateStore) Nodes(ws memdb.WatchSet) (memdb.ResultIterator, error) { 828 txn := s.db.Txn(false) 829 830 // Walk the entire nodes table 831 iter, err := txn.Get("nodes", "id") 832 if err != nil { 833 return nil, err 834 } 835 ws.Add(iter.WatchCh()) 836 return iter, nil 837 } 838 839 // UpsertJob is used to register a job or update a job definition 840 func (s *StateStore) UpsertJob(index uint64, job *structs.Job) error { 841 txn := s.db.Txn(true) 842 defer txn.Abort() 843 if err := s.upsertJobImpl(index, job, false, txn); err != nil { 844 return err 845 } 846 txn.Commit() 847 return nil 848 } 849 850 // upsertJobImpl is the implementation for registering a job or updating a job definition 851 func (s *StateStore) upsertJobImpl(index uint64, job *structs.Job, keepVersion bool, txn *memdb.Txn) error { 852 // COMPAT 0.7: Upgrade old objects that do not have namespaces 853 if job.Namespace == "" { 854 job.Namespace = structs.DefaultNamespace 855 } 856 857 // Assert the namespace exists 858 if exists, err := s.namespaceExists(txn, job.Namespace); err != nil { 859 return err 860 } else if !exists { 861 return fmt.Errorf("job %q is in nonexistent namespace %q", job.ID, job.Namespace) 862 } 863 864 // Check if the job already exists 865 existing, err := txn.First("jobs", "id", job.Namespace, job.ID) 866 if err != nil { 867 return fmt.Errorf("job lookup failed: %v", err) 868 } 869 870 // Setup the indexes correctly 871 if existing != nil { 872 job.CreateIndex = existing.(*structs.Job).CreateIndex 873 job.ModifyIndex = index 874 875 // Bump the version unless asked to keep it. This should only be done 876 // when changing an internal field such as Stable. A spec change should 877 // always come with a version bump 878 if !keepVersion { 879 job.JobModifyIndex = index 880 job.Version = existing.(*structs.Job).Version + 1 881 } 882 883 // Compute the job status 884 var err error 885 job.Status, err = s.getJobStatus(txn, job, false) 886 if err != nil { 887 return fmt.Errorf("setting job status for %q failed: %v", job.ID, err) 888 } 889 } else { 890 job.CreateIndex = index 891 job.ModifyIndex = index 892 job.JobModifyIndex = index 893 job.Version = 0 894 895 if err := s.setJobStatus(index, txn, job, false, ""); err != nil { 896 return fmt.Errorf("setting job status for %q failed: %v", job.ID, err) 897 } 898 899 // Have to get the job again since it could have been updated 900 updated, err := txn.First("jobs", "id", job.Namespace, job.ID) 901 if err != nil { 902 return fmt.Errorf("job lookup failed: %v", err) 903 } 904 if updated != nil { 905 job = updated.(*structs.Job) 906 } 907 } 908 909 if err := s.updateSummaryWithJob(index, job, txn); err != nil { 910 return fmt.Errorf("unable to create job summary: %v", err) 911 } 912 913 if err := s.upsertJobVersion(index, job, txn); err != nil { 914 return fmt.Errorf("unable to upsert job into job_version table: %v", err) 915 } 916 917 // Create the EphemeralDisk if it's nil by adding up DiskMB from task resources. 918 // COMPAT 0.4.1 -> 0.5 919 s.addEphemeralDiskToTaskGroups(job) 920 921 // Insert the job 922 if err := txn.Insert("jobs", job); err != nil { 923 return fmt.Errorf("job insert failed: %v", err) 924 } 925 if err := txn.Insert("index", &IndexEntry{"jobs", index}); err != nil { 926 return fmt.Errorf("index update failed: %v", err) 927 } 928 929 return nil 930 } 931 932 // DeleteJob is used to deregister a job 933 func (s *StateStore) DeleteJob(index uint64, namespace, jobID string) error { 934 txn := s.db.Txn(true) 935 defer txn.Abort() 936 937 // COMPAT 0.7: Upgrade old objects that do not have namespaces 938 if namespace == "" { 939 namespace = structs.DefaultNamespace 940 } 941 942 // Lookup the node 943 existing, err := txn.First("jobs", "id", namespace, jobID) 944 if err != nil { 945 return fmt.Errorf("job lookup failed: %v", err) 946 } 947 if existing == nil { 948 return fmt.Errorf("job not found") 949 } 950 951 // Check if we should update a parent job summary 952 job := existing.(*structs.Job) 953 if job.ParentID != "" { 954 summaryRaw, err := txn.First("job_summary", "id", namespace, job.ParentID) 955 if err != nil { 956 return fmt.Errorf("unable to retrieve summary for parent job: %v", err) 957 } 958 959 // Only continue if the summary exists. It could not exist if the parent 960 // job was removed 961 if summaryRaw != nil { 962 existing := summaryRaw.(*structs.JobSummary) 963 pSummary := existing.Copy() 964 if pSummary.Children != nil { 965 966 modified := false 967 switch job.Status { 968 case structs.JobStatusPending: 969 pSummary.Children.Pending-- 970 pSummary.Children.Dead++ 971 modified = true 972 case structs.JobStatusRunning: 973 pSummary.Children.Running-- 974 pSummary.Children.Dead++ 975 modified = true 976 case structs.JobStatusDead: 977 default: 978 return fmt.Errorf("unknown old job status %q", job.Status) 979 } 980 981 if modified { 982 // Update the modify index 983 pSummary.ModifyIndex = index 984 985 // COMPAT 0.7: Upgrade old objects that do not have namespaces 986 if pSummary.Namespace == "" { 987 pSummary.Namespace = structs.DefaultNamespace 988 } 989 990 // Insert the summary 991 if err := txn.Insert("job_summary", pSummary); err != nil { 992 return fmt.Errorf("job summary insert failed: %v", err) 993 } 994 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 995 return fmt.Errorf("index update failed: %v", err) 996 } 997 } 998 } 999 } 1000 } 1001 1002 // Delete the job 1003 if err := txn.Delete("jobs", existing); err != nil { 1004 return fmt.Errorf("job delete failed: %v", err) 1005 } 1006 if err := txn.Insert("index", &IndexEntry{"jobs", index}); err != nil { 1007 return fmt.Errorf("index update failed: %v", err) 1008 } 1009 1010 // Delete the job versions 1011 if err := s.deleteJobVersions(index, job, txn); err != nil { 1012 return err 1013 } 1014 1015 // Delete the job summary 1016 if _, err = txn.DeleteAll("job_summary", "id", namespace, jobID); err != nil { 1017 return fmt.Errorf("deleing job summary failed: %v", err) 1018 } 1019 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 1020 return fmt.Errorf("index update failed: %v", err) 1021 } 1022 1023 txn.Commit() 1024 return nil 1025 } 1026 1027 // deleteJobVersions deletes all versions of the given job. 1028 func (s *StateStore) deleteJobVersions(index uint64, job *structs.Job, txn *memdb.Txn) error { 1029 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1030 if job.Namespace == "" { 1031 job.Namespace = structs.DefaultNamespace 1032 } 1033 1034 iter, err := txn.Get("job_version", "id_prefix", job.Namespace, job.ID) 1035 if err != nil { 1036 return err 1037 } 1038 1039 for { 1040 raw := iter.Next() 1041 if raw == nil { 1042 break 1043 } 1044 1045 // Ensure the ID is an exact match 1046 j := raw.(*structs.Job) 1047 if j.ID != job.ID { 1048 continue 1049 } 1050 1051 if _, err = txn.DeleteAll("job_version", "id", j.Namespace, j.ID, j.Version); err != nil { 1052 return fmt.Errorf("deleting job versions failed: %v", err) 1053 } 1054 } 1055 1056 if err := txn.Insert("index", &IndexEntry{"job_version", index}); err != nil { 1057 return fmt.Errorf("index update failed: %v", err) 1058 } 1059 1060 return nil 1061 } 1062 1063 // upsertJobVersion inserts a job into its historic version table and limits the 1064 // number of job versions that are tracked. 1065 func (s *StateStore) upsertJobVersion(index uint64, job *structs.Job, txn *memdb.Txn) error { 1066 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1067 if job.Namespace == "" { 1068 job.Namespace = structs.DefaultNamespace 1069 } 1070 1071 // Insert the job 1072 if err := txn.Insert("job_version", job); err != nil { 1073 return fmt.Errorf("failed to insert job into job_version table: %v", err) 1074 } 1075 1076 if err := txn.Insert("index", &IndexEntry{"job_version", index}); err != nil { 1077 return fmt.Errorf("index update failed: %v", err) 1078 } 1079 1080 // Get all the historic jobs for this ID 1081 all, err := s.jobVersionByID(txn, nil, job.Namespace, job.ID) 1082 if err != nil { 1083 return fmt.Errorf("failed to look up job versions for %q: %v", job.ID, err) 1084 } 1085 1086 // If we are below the limit there is no GCing to be done 1087 if len(all) <= structs.JobTrackedVersions { 1088 return nil 1089 } 1090 1091 // We have to delete a historic job to make room. 1092 // Find index of the highest versioned stable job 1093 stableIdx := -1 1094 for i, j := range all { 1095 if j.Stable { 1096 stableIdx = i 1097 break 1098 } 1099 } 1100 1101 // If the stable job is the oldest version, do a swap to bring it into the 1102 // keep set. 1103 max := structs.JobTrackedVersions 1104 if stableIdx == max { 1105 all[max-1], all[max] = all[max], all[max-1] 1106 } 1107 1108 // Delete the job outside of the set that are being kept. 1109 d := all[max] 1110 if err := txn.Delete("job_version", d); err != nil { 1111 return fmt.Errorf("failed to delete job %v (%d) from job_version", d.ID, d.Version) 1112 } 1113 1114 return nil 1115 } 1116 1117 // JobByID is used to lookup a job by its ID. JobByID returns the current/latest job 1118 // version. 1119 func (s *StateStore) JobByID(ws memdb.WatchSet, namespace, id string) (*structs.Job, error) { 1120 txn := s.db.Txn(false) 1121 1122 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1123 if namespace == "" { 1124 namespace = structs.DefaultNamespace 1125 } 1126 1127 watchCh, existing, err := txn.FirstWatch("jobs", "id", namespace, id) 1128 if err != nil { 1129 return nil, fmt.Errorf("job lookup failed: %v", err) 1130 } 1131 ws.Add(watchCh) 1132 1133 if existing != nil { 1134 return existing.(*structs.Job), nil 1135 } 1136 return nil, nil 1137 } 1138 1139 // JobsByIDPrefix is used to lookup a job by prefix 1140 func (s *StateStore) JobsByIDPrefix(ws memdb.WatchSet, namespace, id string) (memdb.ResultIterator, error) { 1141 txn := s.db.Txn(false) 1142 1143 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1144 if namespace == "" { 1145 namespace = structs.DefaultNamespace 1146 } 1147 1148 iter, err := txn.Get("jobs", "id_prefix", namespace, id) 1149 if err != nil { 1150 return nil, fmt.Errorf("job lookup failed: %v", err) 1151 } 1152 1153 ws.Add(iter.WatchCh()) 1154 1155 return iter, nil 1156 } 1157 1158 // JobVersionsByID returns all the tracked versions of a job. 1159 func (s *StateStore) JobVersionsByID(ws memdb.WatchSet, namespace, id string) ([]*structs.Job, error) { 1160 txn := s.db.Txn(false) 1161 1162 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1163 if namespace == "" { 1164 namespace = structs.DefaultNamespace 1165 } 1166 1167 return s.jobVersionByID(txn, &ws, namespace, id) 1168 } 1169 1170 // jobVersionByID is the underlying implementation for retrieving all tracked 1171 // versions of a job and is called under an existing transaction. A watch set 1172 // can optionally be passed in to add the job histories to the watch set. 1173 func (s *StateStore) jobVersionByID(txn *memdb.Txn, ws *memdb.WatchSet, namespace, id string) ([]*structs.Job, error) { 1174 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1175 if namespace == "" { 1176 namespace = structs.DefaultNamespace 1177 } 1178 1179 // Get all the historic jobs for this ID 1180 iter, err := txn.Get("job_version", "id_prefix", namespace, id) 1181 if err != nil { 1182 return nil, err 1183 } 1184 1185 if ws != nil { 1186 ws.Add(iter.WatchCh()) 1187 } 1188 1189 var all []*structs.Job 1190 for { 1191 raw := iter.Next() 1192 if raw == nil { 1193 break 1194 } 1195 1196 // Ensure the ID is an exact match 1197 j := raw.(*structs.Job) 1198 if j.ID != id { 1199 continue 1200 } 1201 1202 all = append(all, j) 1203 } 1204 1205 // Sort in reverse order so that the highest version is first 1206 sort.Slice(all, func(i, j int) bool { 1207 return all[i].Version > all[j].Version 1208 }) 1209 1210 return all, nil 1211 } 1212 1213 // JobByIDAndVersion returns the job identified by its ID and Version. The 1214 // passed watchset may be nil. 1215 func (s *StateStore) JobByIDAndVersion(ws memdb.WatchSet, namespace, id string, version uint64) (*structs.Job, error) { 1216 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1217 if namespace == "" { 1218 namespace = structs.DefaultNamespace 1219 } 1220 txn := s.db.Txn(false) 1221 return s.jobByIDAndVersionImpl(ws, namespace, id, version, txn) 1222 } 1223 1224 // jobByIDAndVersionImpl returns the job identified by its ID and Version. The 1225 // passed watchset may be nil. 1226 func (s *StateStore) jobByIDAndVersionImpl(ws memdb.WatchSet, namespace, id string, 1227 version uint64, txn *memdb.Txn) (*structs.Job, error) { 1228 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1229 if namespace == "" { 1230 namespace = structs.DefaultNamespace 1231 } 1232 1233 watchCh, existing, err := txn.FirstWatch("job_version", "id", namespace, id, version) 1234 if err != nil { 1235 return nil, err 1236 } 1237 1238 if ws != nil { 1239 ws.Add(watchCh) 1240 } 1241 1242 if existing != nil { 1243 job := existing.(*structs.Job) 1244 return job, nil 1245 } 1246 1247 return nil, nil 1248 } 1249 1250 func (s *StateStore) JobVersions(ws memdb.WatchSet) (memdb.ResultIterator, error) { 1251 txn := s.db.Txn(false) 1252 1253 // Walk the entire deployments table 1254 iter, err := txn.Get("job_version", "id") 1255 if err != nil { 1256 return nil, err 1257 } 1258 1259 ws.Add(iter.WatchCh()) 1260 return iter, nil 1261 } 1262 1263 // Jobs returns an iterator over all the jobs 1264 func (s *StateStore) Jobs(ws memdb.WatchSet) (memdb.ResultIterator, error) { 1265 txn := s.db.Txn(false) 1266 1267 // Walk the entire jobs table 1268 iter, err := txn.Get("jobs", "id") 1269 if err != nil { 1270 return nil, err 1271 } 1272 1273 ws.Add(iter.WatchCh()) 1274 1275 return iter, nil 1276 } 1277 1278 // JobsByNamespace returns an iterator over all the jobs for the given namespace 1279 func (s *StateStore) JobsByNamespace(ws memdb.WatchSet, namespace string) (memdb.ResultIterator, error) { 1280 txn := s.db.Txn(false) 1281 return s.jobsByNamespaceImpl(ws, namespace, txn) 1282 } 1283 1284 // jobsByNamespaceImpl returns an iterator over all the jobs for the given namespace 1285 func (s *StateStore) jobsByNamespaceImpl(ws memdb.WatchSet, namespace string, txn *memdb.Txn) (memdb.ResultIterator, error) { 1286 // Walk the entire jobs table 1287 iter, err := txn.Get("jobs", "id_prefix", namespace, "") 1288 if err != nil { 1289 return nil, err 1290 } 1291 1292 ws.Add(iter.WatchCh()) 1293 1294 return iter, nil 1295 } 1296 1297 // JobsByPeriodic returns an iterator over all the periodic or non-periodic jobs. 1298 func (s *StateStore) JobsByPeriodic(ws memdb.WatchSet, periodic bool) (memdb.ResultIterator, error) { 1299 txn := s.db.Txn(false) 1300 1301 iter, err := txn.Get("jobs", "periodic", periodic) 1302 if err != nil { 1303 return nil, err 1304 } 1305 1306 ws.Add(iter.WatchCh()) 1307 1308 return iter, nil 1309 } 1310 1311 // JobsByScheduler returns an iterator over all the jobs with the specific 1312 // scheduler type. 1313 func (s *StateStore) JobsByScheduler(ws memdb.WatchSet, schedulerType string) (memdb.ResultIterator, error) { 1314 txn := s.db.Txn(false) 1315 1316 // Return an iterator for jobs with the specific type. 1317 iter, err := txn.Get("jobs", "type", schedulerType) 1318 if err != nil { 1319 return nil, err 1320 } 1321 1322 ws.Add(iter.WatchCh()) 1323 1324 return iter, nil 1325 } 1326 1327 // JobsByGC returns an iterator over all jobs eligible or uneligible for garbage 1328 // collection. 1329 func (s *StateStore) JobsByGC(ws memdb.WatchSet, gc bool) (memdb.ResultIterator, error) { 1330 txn := s.db.Txn(false) 1331 1332 iter, err := txn.Get("jobs", "gc", gc) 1333 if err != nil { 1334 return nil, err 1335 } 1336 1337 ws.Add(iter.WatchCh()) 1338 1339 return iter, nil 1340 } 1341 1342 // JobSummary returns a job summary object which matches a specific id. 1343 func (s *StateStore) JobSummaryByID(ws memdb.WatchSet, namespace, jobID string) (*structs.JobSummary, error) { 1344 txn := s.db.Txn(false) 1345 1346 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1347 if namespace == "" { 1348 namespace = structs.DefaultNamespace 1349 } 1350 1351 watchCh, existing, err := txn.FirstWatch("job_summary", "id", namespace, jobID) 1352 if err != nil { 1353 return nil, err 1354 } 1355 1356 ws.Add(watchCh) 1357 1358 if existing != nil { 1359 summary := existing.(*structs.JobSummary) 1360 return summary, nil 1361 } 1362 1363 return nil, nil 1364 } 1365 1366 // JobSummaries walks the entire job summary table and returns all the job 1367 // summary objects 1368 func (s *StateStore) JobSummaries(ws memdb.WatchSet) (memdb.ResultIterator, error) { 1369 txn := s.db.Txn(false) 1370 1371 iter, err := txn.Get("job_summary", "id") 1372 if err != nil { 1373 return nil, err 1374 } 1375 1376 ws.Add(iter.WatchCh()) 1377 1378 return iter, nil 1379 } 1380 1381 // JobSummaryByPrefix is used to look up Job Summary by id prefix 1382 func (s *StateStore) JobSummaryByPrefix(ws memdb.WatchSet, namespace, id string) (memdb.ResultIterator, error) { 1383 txn := s.db.Txn(false) 1384 1385 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1386 if namespace == "" { 1387 namespace = structs.DefaultNamespace 1388 } 1389 1390 iter, err := txn.Get("job_summary", "id_prefix", namespace, id) 1391 if err != nil { 1392 return nil, fmt.Errorf("eval lookup failed: %v", err) 1393 } 1394 1395 ws.Add(iter.WatchCh()) 1396 1397 return iter, nil 1398 } 1399 1400 // UpsertPeriodicLaunch is used to register a launch or update it. 1401 func (s *StateStore) UpsertPeriodicLaunch(index uint64, launch *structs.PeriodicLaunch) error { 1402 txn := s.db.Txn(true) 1403 defer txn.Abort() 1404 1405 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1406 if launch.Namespace == "" { 1407 launch.Namespace = structs.DefaultNamespace 1408 } 1409 1410 // Check if the job already exists 1411 existing, err := txn.First("periodic_launch", "id", launch.Namespace, launch.ID) 1412 if err != nil { 1413 return fmt.Errorf("periodic launch lookup failed: %v", err) 1414 } 1415 1416 // Setup the indexes correctly 1417 if existing != nil { 1418 launch.CreateIndex = existing.(*structs.PeriodicLaunch).CreateIndex 1419 launch.ModifyIndex = index 1420 } else { 1421 launch.CreateIndex = index 1422 launch.ModifyIndex = index 1423 } 1424 1425 // Insert the job 1426 if err := txn.Insert("periodic_launch", launch); err != nil { 1427 return fmt.Errorf("launch insert failed: %v", err) 1428 } 1429 if err := txn.Insert("index", &IndexEntry{"periodic_launch", index}); err != nil { 1430 return fmt.Errorf("index update failed: %v", err) 1431 } 1432 1433 txn.Commit() 1434 return nil 1435 } 1436 1437 // DeletePeriodicLaunch is used to delete the periodic launch 1438 func (s *StateStore) DeletePeriodicLaunch(index uint64, namespace, jobID string) error { 1439 txn := s.db.Txn(true) 1440 defer txn.Abort() 1441 1442 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1443 if namespace == "" { 1444 namespace = structs.DefaultNamespace 1445 } 1446 1447 // Lookup the launch 1448 existing, err := txn.First("periodic_launch", "id", namespace, jobID) 1449 if err != nil { 1450 return fmt.Errorf("launch lookup failed: %v", err) 1451 } 1452 if existing == nil { 1453 return fmt.Errorf("launch not found") 1454 } 1455 1456 // Delete the launch 1457 if err := txn.Delete("periodic_launch", existing); err != nil { 1458 return fmt.Errorf("launch delete failed: %v", err) 1459 } 1460 if err := txn.Insert("index", &IndexEntry{"periodic_launch", index}); err != nil { 1461 return fmt.Errorf("index update failed: %v", err) 1462 } 1463 1464 txn.Commit() 1465 return nil 1466 } 1467 1468 // PeriodicLaunchByID is used to lookup a periodic launch by the periodic job 1469 // ID. 1470 func (s *StateStore) PeriodicLaunchByID(ws memdb.WatchSet, namespace, id string) (*structs.PeriodicLaunch, error) { 1471 txn := s.db.Txn(false) 1472 1473 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1474 if namespace == "" { 1475 namespace = structs.DefaultNamespace 1476 } 1477 1478 watchCh, existing, err := txn.FirstWatch("periodic_launch", "id", namespace, id) 1479 if err != nil { 1480 return nil, fmt.Errorf("periodic launch lookup failed: %v", err) 1481 } 1482 1483 ws.Add(watchCh) 1484 1485 if existing != nil { 1486 return existing.(*structs.PeriodicLaunch), nil 1487 } 1488 return nil, nil 1489 } 1490 1491 // PeriodicLaunches returns an iterator over all the periodic launches 1492 func (s *StateStore) PeriodicLaunches(ws memdb.WatchSet) (memdb.ResultIterator, error) { 1493 txn := s.db.Txn(false) 1494 1495 // Walk the entire table 1496 iter, err := txn.Get("periodic_launch", "id") 1497 if err != nil { 1498 return nil, err 1499 } 1500 1501 ws.Add(iter.WatchCh()) 1502 1503 return iter, nil 1504 } 1505 1506 // UpsertEvals is used to upsert a set of evaluations 1507 func (s *StateStore) UpsertEvals(index uint64, evals []*structs.Evaluation) error { 1508 txn := s.db.Txn(true) 1509 defer txn.Abort() 1510 1511 // Do a nested upsert 1512 jobs := make(map[structs.NamespacedID]string, len(evals)) 1513 for _, eval := range evals { 1514 if err := s.nestedUpsertEval(txn, index, eval); err != nil { 1515 return err 1516 } 1517 1518 tuple := structs.NamespacedID{ 1519 ID: eval.JobID, 1520 Namespace: eval.Namespace, 1521 } 1522 jobs[tuple] = "" 1523 } 1524 1525 // Set the job's status 1526 if err := s.setJobStatuses(index, txn, jobs, false); err != nil { 1527 return fmt.Errorf("setting job status failed: %v", err) 1528 } 1529 1530 txn.Commit() 1531 return nil 1532 } 1533 1534 // nestedUpsertEvaluation is used to nest an evaluation upsert within a transaction 1535 func (s *StateStore) nestedUpsertEval(txn *memdb.Txn, index uint64, eval *structs.Evaluation) error { 1536 // Lookup the evaluation 1537 existing, err := txn.First("evals", "id", eval.ID) 1538 if err != nil { 1539 return fmt.Errorf("eval lookup failed: %v", err) 1540 } 1541 1542 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1543 if eval.Namespace == "" { 1544 eval.Namespace = structs.DefaultNamespace 1545 } 1546 1547 // Update the indexes 1548 if existing != nil { 1549 eval.CreateIndex = existing.(*structs.Evaluation).CreateIndex 1550 eval.ModifyIndex = index 1551 } else { 1552 eval.CreateIndex = index 1553 eval.ModifyIndex = index 1554 } 1555 1556 // Update the job summary 1557 summaryRaw, err := txn.First("job_summary", "id", eval.Namespace, eval.JobID) 1558 if err != nil { 1559 return fmt.Errorf("job summary lookup failed: %v", err) 1560 } 1561 if summaryRaw != nil { 1562 js := summaryRaw.(*structs.JobSummary).Copy() 1563 hasSummaryChanged := false 1564 for tg, num := range eval.QueuedAllocations { 1565 if summary, ok := js.Summary[tg]; ok { 1566 if summary.Queued != num { 1567 summary.Queued = num 1568 js.Summary[tg] = summary 1569 hasSummaryChanged = true 1570 } 1571 } else { 1572 s.logger.Printf("[ERR] state_store: unable to update queued for job %q and task group %q", eval.JobID, tg) 1573 } 1574 } 1575 1576 // Insert the job summary 1577 if hasSummaryChanged { 1578 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1579 if js.Namespace == "" { 1580 js.Namespace = structs.DefaultNamespace 1581 } 1582 1583 js.ModifyIndex = index 1584 if err := txn.Insert("job_summary", js); err != nil { 1585 return fmt.Errorf("job summary insert failed: %v", err) 1586 } 1587 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 1588 return fmt.Errorf("index update failed: %v", err) 1589 } 1590 } 1591 } 1592 1593 // Check if the job has any blocked evaluations and cancel them 1594 if eval.Status == structs.EvalStatusComplete && len(eval.FailedTGAllocs) == 0 { 1595 // Get the blocked evaluation for a job if it exists 1596 iter, err := txn.Get("evals", "job", eval.Namespace, eval.JobID, structs.EvalStatusBlocked) 1597 if err != nil { 1598 return fmt.Errorf("failed to get blocked evals for job %q in namespace %q: %v", eval.JobID, eval.Namespace, err) 1599 } 1600 1601 var blocked []*structs.Evaluation 1602 for { 1603 raw := iter.Next() 1604 if raw == nil { 1605 break 1606 } 1607 blocked = append(blocked, raw.(*structs.Evaluation)) 1608 } 1609 1610 // Go through and update the evals 1611 for _, eval := range blocked { 1612 newEval := eval.Copy() 1613 newEval.Status = structs.EvalStatusCancelled 1614 newEval.StatusDescription = fmt.Sprintf("evaluation %q successful", newEval.ID) 1615 newEval.ModifyIndex = index 1616 1617 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1618 if newEval.Namespace == "" { 1619 newEval.Namespace = structs.DefaultNamespace 1620 } 1621 1622 if err := txn.Insert("evals", newEval); err != nil { 1623 return fmt.Errorf("eval insert failed: %v", err) 1624 } 1625 } 1626 } 1627 1628 // Insert the eval 1629 if err := txn.Insert("evals", eval); err != nil { 1630 return fmt.Errorf("eval insert failed: %v", err) 1631 } 1632 if err := txn.Insert("index", &IndexEntry{"evals", index}); err != nil { 1633 return fmt.Errorf("index update failed: %v", err) 1634 } 1635 return nil 1636 } 1637 1638 // updateEvalModifyIndex is used to update the modify index of an evaluation that has been 1639 // through a scheduler pass. This is done as part of plan apply. It ensures that when a subsequent 1640 // scheduler workers process a re-queued evaluation it sees any partial updates from the plan apply. 1641 func (s *StateStore) updateEvalModifyIndex(txn *memdb.Txn, index uint64, evalID string) error { 1642 // Lookup the evaluation 1643 existing, err := txn.First("evals", "id", evalID) 1644 if err != nil { 1645 return fmt.Errorf("eval lookup failed: %v", err) 1646 } 1647 if existing == nil { 1648 err := fmt.Errorf("unable to find eval id %q", evalID) 1649 s.logger.Printf("[ERR] state_store: %v", err) 1650 return err 1651 } 1652 eval := existing.(*structs.Evaluation).Copy() 1653 // Update the indexes 1654 eval.ModifyIndex = index 1655 1656 // Insert the eval 1657 if err := txn.Insert("evals", eval); err != nil { 1658 return fmt.Errorf("eval insert failed: %v", err) 1659 } 1660 if err := txn.Insert("index", &IndexEntry{"evals", index}); err != nil { 1661 return fmt.Errorf("index update failed: %v", err) 1662 } 1663 return nil 1664 } 1665 1666 // DeleteEval is used to delete an evaluation 1667 func (s *StateStore) DeleteEval(index uint64, evals []string, allocs []string) error { 1668 txn := s.db.Txn(true) 1669 defer txn.Abort() 1670 1671 jobs := make(map[structs.NamespacedID]string, len(evals)) 1672 for _, eval := range evals { 1673 existing, err := txn.First("evals", "id", eval) 1674 if err != nil { 1675 return fmt.Errorf("eval lookup failed: %v", err) 1676 } 1677 if existing == nil { 1678 continue 1679 } 1680 if err := txn.Delete("evals", existing); err != nil { 1681 return fmt.Errorf("eval delete failed: %v", err) 1682 } 1683 eval := existing.(*structs.Evaluation) 1684 1685 tuple := structs.NamespacedID{ 1686 ID: eval.JobID, 1687 Namespace: eval.Namespace, 1688 } 1689 jobs[tuple] = "" 1690 } 1691 1692 for _, alloc := range allocs { 1693 raw, err := txn.First("allocs", "id", alloc) 1694 if err != nil { 1695 return fmt.Errorf("alloc lookup failed: %v", err) 1696 } 1697 if raw == nil { 1698 continue 1699 } 1700 if err := txn.Delete("allocs", raw); err != nil { 1701 return fmt.Errorf("alloc delete failed: %v", err) 1702 } 1703 } 1704 1705 // Update the indexes 1706 if err := txn.Insert("index", &IndexEntry{"evals", index}); err != nil { 1707 return fmt.Errorf("index update failed: %v", err) 1708 } 1709 if err := txn.Insert("index", &IndexEntry{"allocs", index}); err != nil { 1710 return fmt.Errorf("index update failed: %v", err) 1711 } 1712 1713 // Set the job's status 1714 if err := s.setJobStatuses(index, txn, jobs, true); err != nil { 1715 return fmt.Errorf("setting job status failed: %v", err) 1716 } 1717 1718 txn.Commit() 1719 return nil 1720 } 1721 1722 // EvalByID is used to lookup an eval by its ID 1723 func (s *StateStore) EvalByID(ws memdb.WatchSet, id string) (*structs.Evaluation, error) { 1724 txn := s.db.Txn(false) 1725 1726 watchCh, existing, err := txn.FirstWatch("evals", "id", id) 1727 if err != nil { 1728 return nil, fmt.Errorf("eval lookup failed: %v", err) 1729 } 1730 1731 ws.Add(watchCh) 1732 1733 if existing != nil { 1734 return existing.(*structs.Evaluation), nil 1735 } 1736 return nil, nil 1737 } 1738 1739 // EvalsByIDPrefix is used to lookup evaluations by prefix in a particular 1740 // namespace 1741 func (s *StateStore) EvalsByIDPrefix(ws memdb.WatchSet, namespace, id string) (memdb.ResultIterator, error) { 1742 txn := s.db.Txn(false) 1743 1744 // Get an iterator over all evals by the id prefix 1745 iter, err := txn.Get("evals", "id_prefix", id) 1746 if err != nil { 1747 return nil, fmt.Errorf("eval lookup failed: %v", err) 1748 } 1749 1750 ws.Add(iter.WatchCh()) 1751 1752 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1753 if namespace == "" { 1754 namespace = structs.DefaultNamespace 1755 } 1756 1757 // Wrap the iterator in a filter 1758 wrap := memdb.NewFilterIterator(iter, evalNamespaceFilter(namespace)) 1759 return wrap, nil 1760 } 1761 1762 // evalNamespaceFilter returns a filter function that filters all evaluations 1763 // not in the given namespace. 1764 func evalNamespaceFilter(namespace string) func(interface{}) bool { 1765 return func(raw interface{}) bool { 1766 eval, ok := raw.(*structs.Evaluation) 1767 if !ok { 1768 return true 1769 } 1770 1771 return eval.Namespace != namespace 1772 } 1773 } 1774 1775 // EvalsByJob returns all the evaluations by job id 1776 func (s *StateStore) EvalsByJob(ws memdb.WatchSet, namespace, jobID string) ([]*structs.Evaluation, error) { 1777 txn := s.db.Txn(false) 1778 1779 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1780 if namespace == "" { 1781 namespace = structs.DefaultNamespace 1782 } 1783 1784 // Get an iterator over the node allocations 1785 iter, err := txn.Get("evals", "job_prefix", namespace, jobID) 1786 if err != nil { 1787 return nil, err 1788 } 1789 1790 ws.Add(iter.WatchCh()) 1791 1792 var out []*structs.Evaluation 1793 for { 1794 raw := iter.Next() 1795 if raw == nil { 1796 break 1797 } 1798 1799 e := raw.(*structs.Evaluation) 1800 1801 // Filter non-exact matches 1802 if e.JobID != jobID { 1803 continue 1804 } 1805 1806 out = append(out, e) 1807 } 1808 return out, nil 1809 } 1810 1811 // Evals returns an iterator over all the evaluations 1812 func (s *StateStore) Evals(ws memdb.WatchSet) (memdb.ResultIterator, error) { 1813 txn := s.db.Txn(false) 1814 1815 // Walk the entire table 1816 iter, err := txn.Get("evals", "id") 1817 if err != nil { 1818 return nil, err 1819 } 1820 1821 ws.Add(iter.WatchCh()) 1822 1823 return iter, nil 1824 } 1825 1826 // EvalsByNamespace returns an iterator over all the evaluations in the given 1827 // namespace 1828 func (s *StateStore) EvalsByNamespace(ws memdb.WatchSet, namespace string) (memdb.ResultIterator, error) { 1829 txn := s.db.Txn(false) 1830 1831 // Walk the entire table 1832 iter, err := txn.Get("evals", "namespace", namespace) 1833 if err != nil { 1834 return nil, err 1835 } 1836 1837 ws.Add(iter.WatchCh()) 1838 1839 return iter, nil 1840 } 1841 1842 // UpdateAllocsFromClient is used to update an allocation based on input 1843 // from a client. While the schedulers are the authority on the allocation for 1844 // most things, some updates are authoritative from the client. Specifically, 1845 // the desired state comes from the schedulers, while the actual state comes 1846 // from clients. 1847 func (s *StateStore) UpdateAllocsFromClient(index uint64, allocs []*structs.Allocation) error { 1848 txn := s.db.Txn(true) 1849 defer txn.Abort() 1850 1851 // Handle each of the updated allocations 1852 for _, alloc := range allocs { 1853 if err := s.nestedUpdateAllocFromClient(txn, index, alloc); err != nil { 1854 return err 1855 } 1856 } 1857 1858 // Update the indexes 1859 if err := txn.Insert("index", &IndexEntry{"allocs", index}); err != nil { 1860 return fmt.Errorf("index update failed: %v", err) 1861 } 1862 1863 txn.Commit() 1864 return nil 1865 } 1866 1867 // nestedUpdateAllocFromClient is used to nest an update of an allocation with client status 1868 func (s *StateStore) nestedUpdateAllocFromClient(txn *memdb.Txn, index uint64, alloc *structs.Allocation) error { 1869 // Look for existing alloc 1870 existing, err := txn.First("allocs", "id", alloc.ID) 1871 if err != nil { 1872 return fmt.Errorf("alloc lookup failed: %v", err) 1873 } 1874 1875 // Nothing to do if this does not exist 1876 if existing == nil { 1877 return nil 1878 } 1879 exist := existing.(*structs.Allocation) 1880 1881 // Copy everything from the existing allocation 1882 copyAlloc := exist.Copy() 1883 1884 // COMPAT 0.7: Upgrade old objects that do not have namespaces 1885 if copyAlloc.Namespace == "" { 1886 copyAlloc.Namespace = structs.DefaultNamespace 1887 } 1888 1889 // Pull in anything the client is the authority on 1890 copyAlloc.ClientStatus = alloc.ClientStatus 1891 copyAlloc.ClientDescription = alloc.ClientDescription 1892 copyAlloc.TaskStates = alloc.TaskStates 1893 copyAlloc.DeploymentStatus = alloc.DeploymentStatus 1894 1895 // Update the modify index 1896 copyAlloc.ModifyIndex = index 1897 1898 // Update the modify time 1899 copyAlloc.ModifyTime = alloc.ModifyTime 1900 1901 if err := s.updateDeploymentWithAlloc(index, copyAlloc, exist, txn); err != nil { 1902 return fmt.Errorf("error updating deployment: %v", err) 1903 } 1904 1905 if err := s.updateSummaryWithAlloc(index, copyAlloc, exist, txn); err != nil { 1906 return fmt.Errorf("error updating job summary: %v", err) 1907 } 1908 1909 if err := s.updateEntWithAlloc(index, copyAlloc, exist, txn); err != nil { 1910 return err 1911 } 1912 1913 // Update the allocation 1914 if err := txn.Insert("allocs", copyAlloc); err != nil { 1915 return fmt.Errorf("alloc insert failed: %v", err) 1916 } 1917 1918 // Set the job's status 1919 forceStatus := "" 1920 if !copyAlloc.TerminalStatus() { 1921 forceStatus = structs.JobStatusRunning 1922 } 1923 1924 tuple := structs.NamespacedID{ 1925 ID: exist.JobID, 1926 Namespace: exist.Namespace, 1927 } 1928 jobs := map[structs.NamespacedID]string{tuple: forceStatus} 1929 1930 if err := s.setJobStatuses(index, txn, jobs, false); err != nil { 1931 return fmt.Errorf("setting job status failed: %v", err) 1932 } 1933 return nil 1934 } 1935 1936 // UpsertAllocs is used to evict a set of allocations and allocate new ones at 1937 // the same time. 1938 func (s *StateStore) UpsertAllocs(index uint64, allocs []*structs.Allocation) error { 1939 txn := s.db.Txn(true) 1940 defer txn.Abort() 1941 if err := s.upsertAllocsImpl(index, allocs, txn); err != nil { 1942 return err 1943 } 1944 txn.Commit() 1945 return nil 1946 } 1947 1948 // upsertAllocs is the actual implementation of UpsertAllocs so that it may be 1949 // used with an existing transaction. 1950 func (s *StateStore) upsertAllocsImpl(index uint64, allocs []*structs.Allocation, txn *memdb.Txn) error { 1951 // Handle the allocations 1952 jobs := make(map[structs.NamespacedID]string, 1) 1953 for _, alloc := range allocs { 1954 existing, err := txn.First("allocs", "id", alloc.ID) 1955 if err != nil { 1956 return fmt.Errorf("alloc lookup failed: %v", err) 1957 } 1958 exist, _ := existing.(*structs.Allocation) 1959 1960 if exist == nil { 1961 alloc.CreateIndex = index 1962 alloc.ModifyIndex = index 1963 alloc.AllocModifyIndex = index 1964 1965 // Issue https://github.com/hashicorp/nomad/issues/2583 uncovered 1966 // the a race between a forced garbage collection and the scheduler 1967 // marking an allocation as terminal. The issue is that the 1968 // allocation from the scheduler has its job normalized and the FSM 1969 // will only denormalize if the allocation is not terminal. However 1970 // if the allocation is garbage collected, that will result in a 1971 // allocation being upserted for the first time without a job 1972 // attached. By returning an error here, it will cause the FSM to 1973 // error, causing the plan_apply to error and thus causing the 1974 // evaluation to be failed. This will force an index refresh that 1975 // should solve this issue. 1976 if alloc.Job == nil { 1977 return fmt.Errorf("attempting to upsert allocation %q without a job", alloc.ID) 1978 } 1979 } else { 1980 alloc.CreateIndex = exist.CreateIndex 1981 alloc.ModifyIndex = index 1982 alloc.AllocModifyIndex = index 1983 1984 // Keep the clients task states 1985 alloc.TaskStates = exist.TaskStates 1986 1987 // If the scheduler is marking this allocation as lost we do not 1988 // want to reuse the status of the existing allocation. 1989 if alloc.ClientStatus != structs.AllocClientStatusLost { 1990 alloc.ClientStatus = exist.ClientStatus 1991 alloc.ClientDescription = exist.ClientDescription 1992 } 1993 1994 // The job has been denormalized so re-attach the original job 1995 if alloc.Job == nil { 1996 alloc.Job = exist.Job 1997 } 1998 } 1999 2000 // COMPAT 0.7: Upgrade old objects that do not have namespaces 2001 if alloc.Namespace == "" { 2002 alloc.Namespace = structs.DefaultNamespace 2003 } 2004 2005 // OPTIMIZATION: 2006 // These should be given a map of new to old allocation and the updates 2007 // should be one on all changes. The current implementation causes O(n) 2008 // lookups/copies/insertions rather than O(1) 2009 if err := s.updateDeploymentWithAlloc(index, alloc, exist, txn); err != nil { 2010 return fmt.Errorf("error updating deployment: %v", err) 2011 } 2012 2013 if err := s.updateSummaryWithAlloc(index, alloc, exist, txn); err != nil { 2014 return fmt.Errorf("error updating job summary: %v", err) 2015 } 2016 2017 if err := s.updateEntWithAlloc(index, alloc, exist, txn); err != nil { 2018 return err 2019 } 2020 2021 // Create the EphemeralDisk if it's nil by adding up DiskMB from task resources. 2022 // COMPAT 0.4.1 -> 0.5 2023 if alloc.Job != nil { 2024 s.addEphemeralDiskToTaskGroups(alloc.Job) 2025 } 2026 2027 if err := txn.Insert("allocs", alloc); err != nil { 2028 return fmt.Errorf("alloc insert failed: %v", err) 2029 } 2030 2031 if alloc.PreviousAllocation != "" { 2032 prevAlloc, err := txn.First("allocs", "id", alloc.PreviousAllocation) 2033 if err != nil { 2034 return fmt.Errorf("alloc lookup failed: %v", err) 2035 } 2036 existingPrevAlloc, _ := prevAlloc.(*structs.Allocation) 2037 if existingPrevAlloc != nil { 2038 prevAllocCopy := existingPrevAlloc.Copy() 2039 prevAllocCopy.NextAllocation = alloc.ID 2040 if err := txn.Insert("allocs", prevAllocCopy); err != nil { 2041 return fmt.Errorf("alloc insert failed: %v", err) 2042 } 2043 } 2044 } 2045 2046 // If the allocation is running, force the job to running status. 2047 forceStatus := "" 2048 if !alloc.TerminalStatus() { 2049 forceStatus = structs.JobStatusRunning 2050 } 2051 2052 tuple := structs.NamespacedID{ 2053 ID: alloc.JobID, 2054 Namespace: alloc.Namespace, 2055 } 2056 jobs[tuple] = forceStatus 2057 } 2058 2059 // Update the indexes 2060 if err := txn.Insert("index", &IndexEntry{"allocs", index}); err != nil { 2061 return fmt.Errorf("index update failed: %v", err) 2062 } 2063 2064 // Set the job's status 2065 if err := s.setJobStatuses(index, txn, jobs, false); err != nil { 2066 return fmt.Errorf("setting job status failed: %v", err) 2067 } 2068 2069 return nil 2070 } 2071 2072 // UpdateAllocsDesiredTransitions is used to update a set of allocations 2073 // desired transitions. 2074 func (s *StateStore) UpdateAllocsDesiredTransitions(index uint64, allocs map[string]*structs.DesiredTransition, 2075 evals []*structs.Evaluation) error { 2076 2077 txn := s.db.Txn(true) 2078 defer txn.Abort() 2079 2080 // Handle each of the updated allocations 2081 for id, transition := range allocs { 2082 if err := s.nestedUpdateAllocDesiredTransition(txn, index, id, transition); err != nil { 2083 return err 2084 } 2085 } 2086 2087 // Update the indexes 2088 if err := txn.Insert("index", &IndexEntry{"allocs", index}); err != nil { 2089 return fmt.Errorf("index update failed: %v", err) 2090 } 2091 2092 txn.Commit() 2093 return nil 2094 } 2095 2096 // nestedUpdateAllocDesiredTransition is used to nest an update of an 2097 // allocations desired transition 2098 func (s *StateStore) nestedUpdateAllocDesiredTransition( 2099 txn *memdb.Txn, index uint64, allocID string, 2100 transition *structs.DesiredTransition) error { 2101 2102 // Look for existing alloc 2103 existing, err := txn.First("allocs", "id", allocID) 2104 if err != nil { 2105 return fmt.Errorf("alloc lookup failed: %v", err) 2106 } 2107 2108 // Nothing to do if this does not exist 2109 if existing == nil { 2110 return nil 2111 } 2112 exist := existing.(*structs.Allocation) 2113 2114 // Copy everything from the existing allocation 2115 copyAlloc := exist.Copy() 2116 2117 // Merge the desired transitions 2118 copyAlloc.DesiredTransition.Merge(transition) 2119 2120 // Update the modify index 2121 copyAlloc.ModifyIndex = index 2122 2123 // Update the allocation 2124 if err := txn.Insert("allocs", copyAlloc); err != nil { 2125 return fmt.Errorf("alloc insert failed: %v", err) 2126 } 2127 2128 return nil 2129 } 2130 2131 // AllocByID is used to lookup an allocation by its ID 2132 func (s *StateStore) AllocByID(ws memdb.WatchSet, id string) (*structs.Allocation, error) { 2133 txn := s.db.Txn(false) 2134 2135 watchCh, existing, err := txn.FirstWatch("allocs", "id", id) 2136 if err != nil { 2137 return nil, fmt.Errorf("alloc lookup failed: %v", err) 2138 } 2139 2140 ws.Add(watchCh) 2141 2142 if existing != nil { 2143 return existing.(*structs.Allocation), nil 2144 } 2145 return nil, nil 2146 } 2147 2148 // AllocsByIDPrefix is used to lookup allocs by prefix 2149 func (s *StateStore) AllocsByIDPrefix(ws memdb.WatchSet, namespace, id string) (memdb.ResultIterator, error) { 2150 txn := s.db.Txn(false) 2151 2152 iter, err := txn.Get("allocs", "id_prefix", id) 2153 if err != nil { 2154 return nil, fmt.Errorf("alloc lookup failed: %v", err) 2155 } 2156 2157 ws.Add(iter.WatchCh()) 2158 2159 // Wrap the iterator in a filter 2160 wrap := memdb.NewFilterIterator(iter, allocNamespaceFilter(namespace)) 2161 return wrap, nil 2162 } 2163 2164 // allocNamespaceFilter returns a filter function that filters all allocations 2165 // not in the given namespace. 2166 func allocNamespaceFilter(namespace string) func(interface{}) bool { 2167 return func(raw interface{}) bool { 2168 alloc, ok := raw.(*structs.Allocation) 2169 if !ok { 2170 return true 2171 } 2172 2173 return alloc.Namespace != namespace 2174 } 2175 } 2176 2177 // AllocsByNode returns all the allocations by node 2178 func (s *StateStore) AllocsByNode(ws memdb.WatchSet, node string) ([]*structs.Allocation, error) { 2179 txn := s.db.Txn(false) 2180 2181 // Get an iterator over the node allocations, using only the 2182 // node prefix which ignores the terminal status 2183 iter, err := txn.Get("allocs", "node_prefix", node) 2184 if err != nil { 2185 return nil, err 2186 } 2187 2188 ws.Add(iter.WatchCh()) 2189 2190 var out []*structs.Allocation 2191 for { 2192 raw := iter.Next() 2193 if raw == nil { 2194 break 2195 } 2196 out = append(out, raw.(*structs.Allocation)) 2197 } 2198 return out, nil 2199 } 2200 2201 // AllocsByNode returns all the allocations by node and terminal status 2202 func (s *StateStore) AllocsByNodeTerminal(ws memdb.WatchSet, node string, terminal bool) ([]*structs.Allocation, error) { 2203 txn := s.db.Txn(false) 2204 2205 // Get an iterator over the node allocations 2206 iter, err := txn.Get("allocs", "node", node, terminal) 2207 if err != nil { 2208 return nil, err 2209 } 2210 2211 ws.Add(iter.WatchCh()) 2212 2213 var out []*structs.Allocation 2214 for { 2215 raw := iter.Next() 2216 if raw == nil { 2217 break 2218 } 2219 out = append(out, raw.(*structs.Allocation)) 2220 } 2221 return out, nil 2222 } 2223 2224 // AllocsByJob returns all the allocations by job id 2225 func (s *StateStore) AllocsByJob(ws memdb.WatchSet, namespace, jobID string, all bool) ([]*structs.Allocation, error) { 2226 txn := s.db.Txn(false) 2227 2228 // COMPAT 0.7: Upgrade old objects that do not have namespaces 2229 if namespace == "" { 2230 namespace = structs.DefaultNamespace 2231 } 2232 2233 // Get the job 2234 var job *structs.Job 2235 rawJob, err := txn.First("jobs", "id", namespace, jobID) 2236 if err != nil { 2237 return nil, err 2238 } 2239 if rawJob != nil { 2240 job = rawJob.(*structs.Job) 2241 } 2242 2243 // Get an iterator over the node allocations 2244 iter, err := txn.Get("allocs", "job", namespace, jobID) 2245 if err != nil { 2246 return nil, err 2247 } 2248 2249 ws.Add(iter.WatchCh()) 2250 2251 var out []*structs.Allocation 2252 for { 2253 raw := iter.Next() 2254 if raw == nil { 2255 break 2256 } 2257 2258 alloc := raw.(*structs.Allocation) 2259 // If the allocation belongs to a job with the same ID but a different 2260 // create index and we are not getting all the allocations whose Jobs 2261 // matches the same Job ID then we skip it 2262 if !all && job != nil && alloc.Job.CreateIndex != job.CreateIndex { 2263 continue 2264 } 2265 out = append(out, raw.(*structs.Allocation)) 2266 } 2267 return out, nil 2268 } 2269 2270 // AllocsByEval returns all the allocations by eval id 2271 func (s *StateStore) AllocsByEval(ws memdb.WatchSet, evalID string) ([]*structs.Allocation, error) { 2272 txn := s.db.Txn(false) 2273 2274 // Get an iterator over the eval allocations 2275 iter, err := txn.Get("allocs", "eval", evalID) 2276 if err != nil { 2277 return nil, err 2278 } 2279 2280 ws.Add(iter.WatchCh()) 2281 2282 var out []*structs.Allocation 2283 for { 2284 raw := iter.Next() 2285 if raw == nil { 2286 break 2287 } 2288 out = append(out, raw.(*structs.Allocation)) 2289 } 2290 return out, nil 2291 } 2292 2293 // AllocsByDeployment returns all the allocations by deployment id 2294 func (s *StateStore) AllocsByDeployment(ws memdb.WatchSet, deploymentID string) ([]*structs.Allocation, error) { 2295 txn := s.db.Txn(false) 2296 2297 // Get an iterator over the deployments allocations 2298 iter, err := txn.Get("allocs", "deployment", deploymentID) 2299 if err != nil { 2300 return nil, err 2301 } 2302 2303 ws.Add(iter.WatchCh()) 2304 2305 var out []*structs.Allocation 2306 for { 2307 raw := iter.Next() 2308 if raw == nil { 2309 break 2310 } 2311 out = append(out, raw.(*structs.Allocation)) 2312 } 2313 return out, nil 2314 } 2315 2316 // Allocs returns an iterator over all the evaluations 2317 func (s *StateStore) Allocs(ws memdb.WatchSet) (memdb.ResultIterator, error) { 2318 txn := s.db.Txn(false) 2319 2320 // Walk the entire table 2321 iter, err := txn.Get("allocs", "id") 2322 if err != nil { 2323 return nil, err 2324 } 2325 2326 ws.Add(iter.WatchCh()) 2327 2328 return iter, nil 2329 } 2330 2331 // AllocsByNamespace returns an iterator over all the allocations in the 2332 // namespace 2333 func (s *StateStore) AllocsByNamespace(ws memdb.WatchSet, namespace string) (memdb.ResultIterator, error) { 2334 txn := s.db.Txn(false) 2335 return s.allocsByNamespaceImpl(ws, txn, namespace) 2336 } 2337 2338 // allocsByNamespaceImpl returns an iterator over all the allocations in the 2339 // namespace 2340 func (s *StateStore) allocsByNamespaceImpl(ws memdb.WatchSet, txn *memdb.Txn, namespace string) (memdb.ResultIterator, error) { 2341 // Walk the entire table 2342 iter, err := txn.Get("allocs", "namespace", namespace) 2343 if err != nil { 2344 return nil, err 2345 } 2346 2347 ws.Add(iter.WatchCh()) 2348 2349 return iter, nil 2350 } 2351 2352 // UpsertVaultAccessors is used to register a set of Vault Accessors 2353 func (s *StateStore) UpsertVaultAccessor(index uint64, accessors []*structs.VaultAccessor) error { 2354 txn := s.db.Txn(true) 2355 defer txn.Abort() 2356 2357 for _, accessor := range accessors { 2358 // Set the create index 2359 accessor.CreateIndex = index 2360 2361 // Insert the accessor 2362 if err := txn.Insert("vault_accessors", accessor); err != nil { 2363 return fmt.Errorf("accessor insert failed: %v", err) 2364 } 2365 } 2366 2367 if err := txn.Insert("index", &IndexEntry{"vault_accessors", index}); err != nil { 2368 return fmt.Errorf("index update failed: %v", err) 2369 } 2370 2371 txn.Commit() 2372 return nil 2373 } 2374 2375 // DeleteVaultAccessors is used to delete a set of Vault Accessors 2376 func (s *StateStore) DeleteVaultAccessors(index uint64, accessors []*structs.VaultAccessor) error { 2377 txn := s.db.Txn(true) 2378 defer txn.Abort() 2379 2380 // Lookup the accessor 2381 for _, accessor := range accessors { 2382 // Delete the accessor 2383 if err := txn.Delete("vault_accessors", accessor); err != nil { 2384 return fmt.Errorf("accessor delete failed: %v", err) 2385 } 2386 } 2387 2388 if err := txn.Insert("index", &IndexEntry{"vault_accessors", index}); err != nil { 2389 return fmt.Errorf("index update failed: %v", err) 2390 } 2391 2392 txn.Commit() 2393 return nil 2394 } 2395 2396 // VaultAccessor returns the given Vault accessor 2397 func (s *StateStore) VaultAccessor(ws memdb.WatchSet, accessor string) (*structs.VaultAccessor, error) { 2398 txn := s.db.Txn(false) 2399 2400 watchCh, existing, err := txn.FirstWatch("vault_accessors", "id", accessor) 2401 if err != nil { 2402 return nil, fmt.Errorf("accessor lookup failed: %v", err) 2403 } 2404 2405 ws.Add(watchCh) 2406 2407 if existing != nil { 2408 return existing.(*structs.VaultAccessor), nil 2409 } 2410 2411 return nil, nil 2412 } 2413 2414 // VaultAccessors returns an iterator of Vault accessors. 2415 func (s *StateStore) VaultAccessors(ws memdb.WatchSet) (memdb.ResultIterator, error) { 2416 txn := s.db.Txn(false) 2417 2418 iter, err := txn.Get("vault_accessors", "id") 2419 if err != nil { 2420 return nil, err 2421 } 2422 2423 ws.Add(iter.WatchCh()) 2424 2425 return iter, nil 2426 } 2427 2428 // VaultAccessorsByAlloc returns all the Vault accessors by alloc id 2429 func (s *StateStore) VaultAccessorsByAlloc(ws memdb.WatchSet, allocID string) ([]*structs.VaultAccessor, error) { 2430 txn := s.db.Txn(false) 2431 2432 // Get an iterator over the accessors 2433 iter, err := txn.Get("vault_accessors", "alloc_id", allocID) 2434 if err != nil { 2435 return nil, err 2436 } 2437 2438 ws.Add(iter.WatchCh()) 2439 2440 var out []*structs.VaultAccessor 2441 for { 2442 raw := iter.Next() 2443 if raw == nil { 2444 break 2445 } 2446 out = append(out, raw.(*structs.VaultAccessor)) 2447 } 2448 return out, nil 2449 } 2450 2451 // VaultAccessorsByNode returns all the Vault accessors by node id 2452 func (s *StateStore) VaultAccessorsByNode(ws memdb.WatchSet, nodeID string) ([]*structs.VaultAccessor, error) { 2453 txn := s.db.Txn(false) 2454 2455 // Get an iterator over the accessors 2456 iter, err := txn.Get("vault_accessors", "node_id", nodeID) 2457 if err != nil { 2458 return nil, err 2459 } 2460 2461 ws.Add(iter.WatchCh()) 2462 2463 var out []*structs.VaultAccessor 2464 for { 2465 raw := iter.Next() 2466 if raw == nil { 2467 break 2468 } 2469 out = append(out, raw.(*structs.VaultAccessor)) 2470 } 2471 return out, nil 2472 } 2473 2474 // UpdateDeploymentStatus is used to make deployment status updates and 2475 // potentially make a evaluation 2476 func (s *StateStore) UpdateDeploymentStatus(index uint64, req *structs.DeploymentStatusUpdateRequest) error { 2477 txn := s.db.Txn(true) 2478 defer txn.Abort() 2479 2480 if err := s.updateDeploymentStatusImpl(index, req.DeploymentUpdate, txn); err != nil { 2481 return err 2482 } 2483 2484 // Upsert the job if necessary 2485 if req.Job != nil { 2486 if err := s.upsertJobImpl(index, req.Job, false, txn); err != nil { 2487 return err 2488 } 2489 } 2490 2491 // Upsert the optional eval 2492 if req.Eval != nil { 2493 if err := s.nestedUpsertEval(txn, index, req.Eval); err != nil { 2494 return err 2495 } 2496 } 2497 2498 txn.Commit() 2499 return nil 2500 } 2501 2502 // updateDeploymentStatusImpl is used to make deployment status updates 2503 func (s *StateStore) updateDeploymentStatusImpl(index uint64, u *structs.DeploymentStatusUpdate, txn *memdb.Txn) error { 2504 // Retrieve deployment 2505 ws := memdb.NewWatchSet() 2506 deployment, err := s.deploymentByIDImpl(ws, u.DeploymentID, txn) 2507 if err != nil { 2508 return err 2509 } else if deployment == nil { 2510 return fmt.Errorf("Deployment ID %q couldn't be updated as it does not exist", u.DeploymentID) 2511 } else if !deployment.Active() { 2512 return fmt.Errorf("Deployment %q has terminal status %q:", deployment.ID, deployment.Status) 2513 } 2514 2515 // Apply the new status 2516 copy := deployment.Copy() 2517 copy.Status = u.Status 2518 copy.StatusDescription = u.StatusDescription 2519 copy.ModifyIndex = index 2520 2521 // COMPAT 0.7: Upgrade old objects that do not have namespaces 2522 if copy.Namespace == "" { 2523 copy.Namespace = structs.DefaultNamespace 2524 } 2525 2526 // Insert the deployment 2527 if err := txn.Insert("deployment", copy); err != nil { 2528 return err 2529 } 2530 2531 // Update the index 2532 if err := txn.Insert("index", &IndexEntry{"deployment", index}); err != nil { 2533 return fmt.Errorf("index update failed: %v", err) 2534 } 2535 2536 // If the deployment is being marked as complete, set the job to stable. 2537 if copy.Status == structs.DeploymentStatusSuccessful { 2538 if err := s.updateJobStabilityImpl(index, copy.Namespace, copy.JobID, copy.JobVersion, true, txn); err != nil { 2539 return fmt.Errorf("failed to update job stability: %v", err) 2540 } 2541 } 2542 2543 return nil 2544 } 2545 2546 // UpdateJobStability updates the stability of the given job and version to the 2547 // desired status. 2548 func (s *StateStore) UpdateJobStability(index uint64, namespace, jobID string, jobVersion uint64, stable bool) error { 2549 txn := s.db.Txn(true) 2550 defer txn.Abort() 2551 2552 // COMPAT 0.7: Upgrade old objects that do not have namespaces 2553 if namespace == "" { 2554 namespace = structs.DefaultNamespace 2555 } 2556 2557 if err := s.updateJobStabilityImpl(index, namespace, jobID, jobVersion, stable, txn); err != nil { 2558 return err 2559 } 2560 2561 txn.Commit() 2562 return nil 2563 } 2564 2565 // updateJobStabilityImpl updates the stability of the given job and version 2566 func (s *StateStore) updateJobStabilityImpl(index uint64, namespace, jobID string, jobVersion uint64, stable bool, txn *memdb.Txn) error { 2567 // COMPAT 0.7: Upgrade old objects that do not have namespaces 2568 if namespace == "" { 2569 namespace = structs.DefaultNamespace 2570 } 2571 2572 // Get the job that is referenced 2573 job, err := s.jobByIDAndVersionImpl(nil, namespace, jobID, jobVersion, txn) 2574 if err != nil { 2575 return err 2576 } 2577 2578 // Has already been cleared, nothing to do 2579 if job == nil { 2580 return nil 2581 } 2582 2583 // If the job already has the desired stability, nothing to do 2584 if job.Stable == stable { 2585 return nil 2586 } 2587 2588 copy := job.Copy() 2589 copy.Stable = stable 2590 return s.upsertJobImpl(index, copy, true, txn) 2591 } 2592 2593 // UpdateDeploymentPromotion is used to promote canaries in a deployment and 2594 // potentially make a evaluation 2595 func (s *StateStore) UpdateDeploymentPromotion(index uint64, req *structs.ApplyDeploymentPromoteRequest) error { 2596 txn := s.db.Txn(true) 2597 defer txn.Abort() 2598 2599 // Retrieve deployment and ensure it is not terminal and is active 2600 ws := memdb.NewWatchSet() 2601 deployment, err := s.deploymentByIDImpl(ws, req.DeploymentID, txn) 2602 if err != nil { 2603 return err 2604 } else if deployment == nil { 2605 return fmt.Errorf("Deployment ID %q couldn't be updated as it does not exist", req.DeploymentID) 2606 } else if !deployment.Active() { 2607 return fmt.Errorf("Deployment %q has terminal status %q:", deployment.ID, deployment.Status) 2608 } 2609 2610 // Retrieve effected allocations 2611 iter, err := txn.Get("allocs", "deployment", req.DeploymentID) 2612 if err != nil { 2613 return err 2614 } 2615 2616 groupIndex := make(map[string]struct{}, len(req.Groups)) 2617 for _, g := range req.Groups { 2618 groupIndex[g] = struct{}{} 2619 } 2620 2621 canaryIndex := make(map[string]struct{}, len(deployment.TaskGroups)) 2622 for _, state := range deployment.TaskGroups { 2623 for _, c := range state.PlacedCanaries { 2624 canaryIndex[c] = struct{}{} 2625 } 2626 } 2627 2628 haveCanaries := false 2629 var unhealthyErr multierror.Error 2630 for { 2631 raw := iter.Next() 2632 if raw == nil { 2633 break 2634 } 2635 2636 alloc := raw.(*structs.Allocation) 2637 2638 // Check that the alloc is a canary 2639 if _, ok := canaryIndex[alloc.ID]; !ok { 2640 continue 2641 } 2642 2643 // Check that the canary is part of a group being promoted 2644 if _, ok := groupIndex[alloc.TaskGroup]; !req.All && !ok { 2645 continue 2646 } 2647 2648 // Ensure the canaries are healthy 2649 if !alloc.DeploymentStatus.IsHealthy() { 2650 multierror.Append(&unhealthyErr, fmt.Errorf("Canary allocation %q for group %q is not healthy", alloc.ID, alloc.TaskGroup)) 2651 continue 2652 } 2653 2654 haveCanaries = true 2655 } 2656 2657 if err := unhealthyErr.ErrorOrNil(); err != nil { 2658 return err 2659 } 2660 2661 if !haveCanaries { 2662 return fmt.Errorf("no canaries to promote") 2663 } 2664 2665 // Update deployment 2666 copy := deployment.Copy() 2667 copy.ModifyIndex = index 2668 for tg, status := range copy.TaskGroups { 2669 _, ok := groupIndex[tg] 2670 if !req.All && !ok { 2671 continue 2672 } 2673 2674 status.Promoted = true 2675 } 2676 2677 // If the deployment no longer needs promotion, update its status 2678 if !copy.RequiresPromotion() && copy.Status == structs.DeploymentStatusRunning { 2679 copy.StatusDescription = structs.DeploymentStatusDescriptionRunning 2680 } 2681 2682 // Insert the deployment 2683 if err := s.upsertDeploymentImpl(index, copy, txn); err != nil { 2684 return err 2685 } 2686 2687 // Upsert the optional eval 2688 if req.Eval != nil { 2689 if err := s.nestedUpsertEval(txn, index, req.Eval); err != nil { 2690 return err 2691 } 2692 } 2693 2694 txn.Commit() 2695 return nil 2696 } 2697 2698 // UpdateDeploymentAllocHealth is used to update the health of allocations as 2699 // part of the deployment and potentially make a evaluation 2700 func (s *StateStore) UpdateDeploymentAllocHealth(index uint64, req *structs.ApplyDeploymentAllocHealthRequest) error { 2701 txn := s.db.Txn(true) 2702 defer txn.Abort() 2703 2704 // Retrieve deployment and ensure it is not terminal and is active 2705 ws := memdb.NewWatchSet() 2706 deployment, err := s.deploymentByIDImpl(ws, req.DeploymentID, txn) 2707 if err != nil { 2708 return err 2709 } else if deployment == nil { 2710 return fmt.Errorf("Deployment ID %q couldn't be updated as it does not exist", req.DeploymentID) 2711 } else if !deployment.Active() { 2712 return fmt.Errorf("Deployment %q has terminal status %q:", deployment.ID, deployment.Status) 2713 } 2714 2715 // Update the health status of each allocation 2716 if total := len(req.HealthyAllocationIDs) + len(req.UnhealthyAllocationIDs); total != 0 { 2717 setAllocHealth := func(id string, healthy bool) error { 2718 existing, err := txn.First("allocs", "id", id) 2719 if err != nil { 2720 return fmt.Errorf("alloc %q lookup failed: %v", id, err) 2721 } 2722 if existing == nil { 2723 return fmt.Errorf("unknown alloc %q", id) 2724 } 2725 2726 old := existing.(*structs.Allocation) 2727 if old.DeploymentID != req.DeploymentID { 2728 return fmt.Errorf("alloc %q is not part of deployment %q", id, req.DeploymentID) 2729 } 2730 2731 // Set the health 2732 copy := old.Copy() 2733 if copy.DeploymentStatus == nil { 2734 copy.DeploymentStatus = &structs.AllocDeploymentStatus{} 2735 } 2736 copy.DeploymentStatus.Healthy = helper.BoolToPtr(healthy) 2737 copy.DeploymentStatus.ModifyIndex = index 2738 2739 if err := s.updateDeploymentWithAlloc(index, copy, old, txn); err != nil { 2740 return fmt.Errorf("error updating deployment: %v", err) 2741 } 2742 2743 if err := txn.Insert("allocs", copy); err != nil { 2744 return fmt.Errorf("alloc insert failed: %v", err) 2745 } 2746 2747 return nil 2748 } 2749 2750 for _, id := range req.HealthyAllocationIDs { 2751 if err := setAllocHealth(id, true); err != nil { 2752 return err 2753 } 2754 } 2755 for _, id := range req.UnhealthyAllocationIDs { 2756 if err := setAllocHealth(id, false); err != nil { 2757 return err 2758 } 2759 } 2760 2761 // Update the indexes 2762 if err := txn.Insert("index", &IndexEntry{"allocs", index}); err != nil { 2763 return fmt.Errorf("index update failed: %v", err) 2764 } 2765 } 2766 2767 // Update the deployment status as needed. 2768 if req.DeploymentUpdate != nil { 2769 if err := s.updateDeploymentStatusImpl(index, req.DeploymentUpdate, txn); err != nil { 2770 return err 2771 } 2772 } 2773 2774 // Upsert the job if necessary 2775 if req.Job != nil { 2776 if err := s.upsertJobImpl(index, req.Job, false, txn); err != nil { 2777 return err 2778 } 2779 } 2780 2781 // Upsert the optional eval 2782 if req.Eval != nil { 2783 if err := s.nestedUpsertEval(txn, index, req.Eval); err != nil { 2784 return err 2785 } 2786 } 2787 2788 txn.Commit() 2789 return nil 2790 } 2791 2792 // LastIndex returns the greatest index value for all indexes 2793 func (s *StateStore) LatestIndex() (uint64, error) { 2794 indexes, err := s.Indexes() 2795 if err != nil { 2796 return 0, err 2797 } 2798 2799 var max uint64 = 0 2800 for { 2801 raw := indexes.Next() 2802 if raw == nil { 2803 break 2804 } 2805 2806 // Prepare the request struct 2807 idx := raw.(*IndexEntry) 2808 2809 // Determine the max 2810 if idx.Value > max { 2811 max = idx.Value 2812 } 2813 } 2814 2815 return max, nil 2816 } 2817 2818 // Index finds the matching index value 2819 func (s *StateStore) Index(name string) (uint64, error) { 2820 txn := s.db.Txn(false) 2821 2822 // Lookup the first matching index 2823 out, err := txn.First("index", "id", name) 2824 if err != nil { 2825 return 0, err 2826 } 2827 if out == nil { 2828 return 0, nil 2829 } 2830 return out.(*IndexEntry).Value, nil 2831 } 2832 2833 // RemoveIndex is a helper method to remove an index for testing purposes 2834 func (s *StateStore) RemoveIndex(name string) error { 2835 txn := s.db.Txn(true) 2836 defer txn.Abort() 2837 2838 if _, err := txn.DeleteAll("index", "id", name); err != nil { 2839 return err 2840 } 2841 2842 txn.Commit() 2843 return nil 2844 } 2845 2846 // Indexes returns an iterator over all the indexes 2847 func (s *StateStore) Indexes() (memdb.ResultIterator, error) { 2848 txn := s.db.Txn(false) 2849 2850 // Walk the entire nodes table 2851 iter, err := txn.Get("index", "id") 2852 if err != nil { 2853 return nil, err 2854 } 2855 return iter, nil 2856 } 2857 2858 // ReconcileJobSummaries re-creates summaries for all jobs present in the state 2859 // store 2860 func (s *StateStore) ReconcileJobSummaries(index uint64) error { 2861 txn := s.db.Txn(true) 2862 defer txn.Abort() 2863 2864 // Get all the jobs 2865 iter, err := txn.Get("jobs", "id") 2866 if err != nil { 2867 return err 2868 } 2869 for { 2870 rawJob := iter.Next() 2871 if rawJob == nil { 2872 break 2873 } 2874 job := rawJob.(*structs.Job) 2875 2876 // Create a job summary for the job 2877 summary := &structs.JobSummary{ 2878 JobID: job.ID, 2879 Namespace: job.Namespace, 2880 Summary: make(map[string]structs.TaskGroupSummary), 2881 } 2882 for _, tg := range job.TaskGroups { 2883 summary.Summary[tg.Name] = structs.TaskGroupSummary{} 2884 } 2885 2886 // COMPAT 0.7: Upgrade old objects that do not have namespaces 2887 if job.Namespace == "" { 2888 job.Namespace = structs.DefaultNamespace 2889 } 2890 2891 // Find all the allocations for the jobs 2892 iterAllocs, err := txn.Get("allocs", "job", job.Namespace, job.ID) 2893 if err != nil { 2894 return err 2895 } 2896 2897 // Calculate the summary for the job 2898 for { 2899 rawAlloc := iterAllocs.Next() 2900 if rawAlloc == nil { 2901 break 2902 } 2903 alloc := rawAlloc.(*structs.Allocation) 2904 2905 // Ignore the allocation if it doesn't belong to the currently 2906 // registered job. The allocation is checked because of issue #2304 2907 if alloc.Job == nil || alloc.Job.CreateIndex != job.CreateIndex { 2908 continue 2909 } 2910 2911 tg := summary.Summary[alloc.TaskGroup] 2912 switch alloc.ClientStatus { 2913 case structs.AllocClientStatusFailed: 2914 tg.Failed += 1 2915 case structs.AllocClientStatusLost: 2916 tg.Lost += 1 2917 case structs.AllocClientStatusComplete: 2918 tg.Complete += 1 2919 case structs.AllocClientStatusRunning: 2920 tg.Running += 1 2921 case structs.AllocClientStatusPending: 2922 tg.Starting += 1 2923 default: 2924 s.logger.Printf("[ERR] state_store: invalid client status: %v in allocation %q", alloc.ClientStatus, alloc.ID) 2925 } 2926 summary.Summary[alloc.TaskGroup] = tg 2927 } 2928 2929 // Set the create index of the summary same as the job's create index 2930 // and the modify index to the current index 2931 summary.CreateIndex = job.CreateIndex 2932 summary.ModifyIndex = index 2933 2934 // Insert the job summary 2935 if err := txn.Insert("job_summary", summary); err != nil { 2936 return fmt.Errorf("error inserting job summary: %v", err) 2937 } 2938 } 2939 2940 // Update the indexes table for job summary 2941 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 2942 return fmt.Errorf("index update failed: %v", err) 2943 } 2944 txn.Commit() 2945 return nil 2946 } 2947 2948 // setJobStatuses is a helper for calling setJobStatus on multiple jobs by ID. 2949 // It takes a map of job IDs to an optional forceStatus string. It returns an 2950 // error if the job doesn't exist or setJobStatus fails. 2951 func (s *StateStore) setJobStatuses(index uint64, txn *memdb.Txn, 2952 jobs map[structs.NamespacedID]string, evalDelete bool) error { 2953 for tuple, forceStatus := range jobs { 2954 // COMPAT 0.7: Upgrade old objects that do not have namespaces 2955 if tuple.Namespace == "" { 2956 tuple.Namespace = structs.DefaultNamespace 2957 } 2958 2959 existing, err := txn.First("jobs", "id", tuple.Namespace, tuple.ID) 2960 if err != nil { 2961 return fmt.Errorf("job lookup failed: %v", err) 2962 } 2963 2964 if existing == nil { 2965 continue 2966 } 2967 2968 if err := s.setJobStatus(index, txn, existing.(*structs.Job), evalDelete, forceStatus); err != nil { 2969 return err 2970 } 2971 } 2972 2973 return nil 2974 } 2975 2976 // setJobStatus sets the status of the job by looking up associated evaluations 2977 // and allocations. evalDelete should be set to true if setJobStatus is being 2978 // called because an evaluation is being deleted (potentially because of garbage 2979 // collection). If forceStatus is non-empty, the job's status will be set to the 2980 // passed status. 2981 func (s *StateStore) setJobStatus(index uint64, txn *memdb.Txn, 2982 job *structs.Job, evalDelete bool, forceStatus string) error { 2983 2984 // Capture the current status so we can check if there is a change 2985 oldStatus := job.Status 2986 if index == job.CreateIndex { 2987 oldStatus = "" 2988 } 2989 newStatus := forceStatus 2990 2991 // If forceStatus is not set, compute the jobs status. 2992 if forceStatus == "" { 2993 var err error 2994 newStatus, err = s.getJobStatus(txn, job, evalDelete) 2995 if err != nil { 2996 return err 2997 } 2998 } 2999 3000 // Fast-path if nothing has changed. 3001 if oldStatus == newStatus { 3002 return nil 3003 } 3004 3005 // Copy and update the existing job 3006 updated := job.Copy() 3007 updated.Status = newStatus 3008 updated.ModifyIndex = index 3009 3010 // COMPAT 0.7: Upgrade old objects that do not have namespaces 3011 if updated.Namespace == "" { 3012 updated.Namespace = structs.DefaultNamespace 3013 } 3014 3015 // Insert the job 3016 if err := txn.Insert("jobs", updated); err != nil { 3017 return fmt.Errorf("job insert failed: %v", err) 3018 } 3019 if err := txn.Insert("index", &IndexEntry{"jobs", index}); err != nil { 3020 return fmt.Errorf("index update failed: %v", err) 3021 } 3022 3023 // Update the children summary 3024 if updated.ParentID != "" { 3025 // Try to update the summary of the parent job summary 3026 summaryRaw, err := txn.First("job_summary", "id", updated.Namespace, updated.ParentID) 3027 if err != nil { 3028 return fmt.Errorf("unable to retrieve summary for parent job: %v", err) 3029 } 3030 3031 // Only continue if the summary exists. It could not exist if the parent 3032 // job was removed 3033 if summaryRaw != nil { 3034 existing := summaryRaw.(*structs.JobSummary) 3035 pSummary := existing.Copy() 3036 if pSummary.Children == nil { 3037 pSummary.Children = new(structs.JobChildrenSummary) 3038 } 3039 3040 // COMPAT 0.7: Upgrade old objects that do not have namespaces 3041 if pSummary.Namespace == "" { 3042 pSummary.Namespace = structs.DefaultNamespace 3043 } 3044 3045 // Determine the transition and update the correct fields 3046 children := pSummary.Children 3047 3048 // Decrement old status 3049 if oldStatus != "" { 3050 switch oldStatus { 3051 case structs.JobStatusPending: 3052 children.Pending-- 3053 case structs.JobStatusRunning: 3054 children.Running-- 3055 case structs.JobStatusDead: 3056 children.Dead-- 3057 default: 3058 return fmt.Errorf("unknown old job status %q", oldStatus) 3059 } 3060 } 3061 3062 // Increment new status 3063 switch newStatus { 3064 case structs.JobStatusPending: 3065 children.Pending++ 3066 case structs.JobStatusRunning: 3067 children.Running++ 3068 case structs.JobStatusDead: 3069 children.Dead++ 3070 default: 3071 return fmt.Errorf("unknown new job status %q", newStatus) 3072 } 3073 3074 // Update the index 3075 pSummary.ModifyIndex = index 3076 3077 // Insert the summary 3078 if err := txn.Insert("job_summary", pSummary); err != nil { 3079 return fmt.Errorf("job summary insert failed: %v", err) 3080 } 3081 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 3082 return fmt.Errorf("index update failed: %v", err) 3083 } 3084 } 3085 } 3086 3087 return nil 3088 } 3089 3090 func (s *StateStore) getJobStatus(txn *memdb.Txn, job *structs.Job, evalDelete bool) (string, error) { 3091 // COMPAT 0.7: Upgrade old objects that do not have namespaces 3092 if job.Namespace == "" { 3093 job.Namespace = structs.DefaultNamespace 3094 } 3095 3096 // System, Periodic and Parameterized jobs are running until explicitly 3097 // stopped 3098 if job.Type == structs.JobTypeSystem || job.IsParameterized() || job.IsPeriodic() { 3099 if job.Stop { 3100 return structs.JobStatusDead, nil 3101 } 3102 3103 return structs.JobStatusRunning, nil 3104 } 3105 3106 allocs, err := txn.Get("allocs", "job", job.Namespace, job.ID) 3107 if err != nil { 3108 return "", err 3109 } 3110 3111 // If there is a non-terminal allocation, the job is running. 3112 hasAlloc := false 3113 for alloc := allocs.Next(); alloc != nil; alloc = allocs.Next() { 3114 hasAlloc = true 3115 if !alloc.(*structs.Allocation).TerminalStatus() { 3116 return structs.JobStatusRunning, nil 3117 } 3118 } 3119 3120 evals, err := txn.Get("evals", "job_prefix", job.Namespace, job.ID) 3121 if err != nil { 3122 return "", err 3123 } 3124 3125 hasEval := false 3126 for raw := evals.Next(); raw != nil; raw = evals.Next() { 3127 e := raw.(*structs.Evaluation) 3128 3129 // Filter non-exact matches 3130 if e.JobID != job.ID { 3131 continue 3132 } 3133 3134 hasEval = true 3135 if !e.TerminalStatus() { 3136 return structs.JobStatusPending, nil 3137 } 3138 } 3139 3140 // The job is dead if all the allocations and evals are terminal or if there 3141 // are no evals because of garbage collection. 3142 if evalDelete || hasEval || hasAlloc { 3143 return structs.JobStatusDead, nil 3144 } 3145 3146 return structs.JobStatusPending, nil 3147 } 3148 3149 // updateSummaryWithJob creates or updates job summaries when new jobs are 3150 // upserted or existing ones are updated 3151 func (s *StateStore) updateSummaryWithJob(index uint64, job *structs.Job, 3152 txn *memdb.Txn) error { 3153 3154 // COMPAT 0.7: Upgrade old objects that do not have namespaces 3155 if job.Namespace == "" { 3156 job.Namespace = structs.DefaultNamespace 3157 } 3158 3159 // Update the job summary 3160 summaryRaw, err := txn.First("job_summary", "id", job.Namespace, job.ID) 3161 if err != nil { 3162 return fmt.Errorf("job summary lookup failed: %v", err) 3163 } 3164 3165 // Get the summary or create if necessary 3166 var summary *structs.JobSummary 3167 hasSummaryChanged := false 3168 if summaryRaw != nil { 3169 summary = summaryRaw.(*structs.JobSummary).Copy() 3170 } else { 3171 summary = &structs.JobSummary{ 3172 JobID: job.ID, 3173 Namespace: job.Namespace, 3174 Summary: make(map[string]structs.TaskGroupSummary), 3175 Children: new(structs.JobChildrenSummary), 3176 CreateIndex: index, 3177 } 3178 hasSummaryChanged = true 3179 } 3180 3181 for _, tg := range job.TaskGroups { 3182 if _, ok := summary.Summary[tg.Name]; !ok { 3183 newSummary := structs.TaskGroupSummary{ 3184 Complete: 0, 3185 Failed: 0, 3186 Running: 0, 3187 Starting: 0, 3188 } 3189 summary.Summary[tg.Name] = newSummary 3190 hasSummaryChanged = true 3191 } 3192 } 3193 3194 // The job summary has changed, so update the modify index. 3195 if hasSummaryChanged { 3196 summary.ModifyIndex = index 3197 3198 // COMPAT 0.7: Upgrade old objects that do not have namespaces 3199 if summary.Namespace == "" { 3200 summary.Namespace = structs.DefaultNamespace 3201 } 3202 3203 // Update the indexes table for job summary 3204 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 3205 return fmt.Errorf("index update failed: %v", err) 3206 } 3207 if err := txn.Insert("job_summary", summary); err != nil { 3208 return err 3209 } 3210 } 3211 3212 return nil 3213 } 3214 3215 // updateDeploymentWithAlloc is used to update the deployment state associated 3216 // with the given allocation. The passed alloc may be updated if the deployment 3217 // status has changed to capture the modify index at which it has changed. 3218 func (s *StateStore) updateDeploymentWithAlloc(index uint64, alloc, existing *structs.Allocation, txn *memdb.Txn) error { 3219 // Nothing to do if the allocation is not associated with a deployment 3220 if alloc.DeploymentID == "" { 3221 return nil 3222 } 3223 3224 // Get the deployment 3225 ws := memdb.NewWatchSet() 3226 deployment, err := s.deploymentByIDImpl(ws, alloc.DeploymentID, txn) 3227 if err != nil { 3228 return err 3229 } 3230 if deployment == nil { 3231 return nil 3232 } 3233 3234 // Retrieve the deployment state object 3235 _, ok := deployment.TaskGroups[alloc.TaskGroup] 3236 if !ok { 3237 // If the task group isn't part of the deployment, the task group wasn't 3238 // part of a rolling update so nothing to do 3239 return nil 3240 } 3241 3242 // Do not modify in-place. Instead keep track of what must be done 3243 placed := 0 3244 healthy := 0 3245 unhealthy := 0 3246 3247 // If there was no existing allocation, this is a placement and we increment 3248 // the placement 3249 existingHealthSet := existing != nil && existing.DeploymentStatus.HasHealth() 3250 allocHealthSet := alloc.DeploymentStatus.HasHealth() 3251 if existing == nil || existing.DeploymentID != alloc.DeploymentID { 3252 placed++ 3253 } else if !existingHealthSet && allocHealthSet { 3254 if *alloc.DeploymentStatus.Healthy { 3255 healthy++ 3256 } else { 3257 unhealthy++ 3258 } 3259 } else if existingHealthSet && allocHealthSet { 3260 // See if it has gone from healthy to unhealthy 3261 if *existing.DeploymentStatus.Healthy && !*alloc.DeploymentStatus.Healthy { 3262 healthy-- 3263 unhealthy++ 3264 } 3265 } 3266 3267 // Nothing to do 3268 if placed == 0 && healthy == 0 && unhealthy == 0 { 3269 return nil 3270 } 3271 3272 // Update the allocation's deployment status modify index 3273 if alloc.DeploymentStatus != nil && healthy+unhealthy != 0 { 3274 alloc.DeploymentStatus.ModifyIndex = index 3275 } 3276 3277 // Create a copy of the deployment object 3278 deploymentCopy := deployment.Copy() 3279 deploymentCopy.ModifyIndex = index 3280 3281 state := deploymentCopy.TaskGroups[alloc.TaskGroup] 3282 state.PlacedAllocs += placed 3283 state.HealthyAllocs += healthy 3284 state.UnhealthyAllocs += unhealthy 3285 3286 // Upsert the deployment 3287 if err := s.upsertDeploymentImpl(index, deploymentCopy, txn); err != nil { 3288 return err 3289 } 3290 3291 return nil 3292 } 3293 3294 // updateSummaryWithAlloc updates the job summary when allocations are updated 3295 // or inserted 3296 func (s *StateStore) updateSummaryWithAlloc(index uint64, alloc *structs.Allocation, 3297 existingAlloc *structs.Allocation, txn *memdb.Txn) error { 3298 3299 // We don't have to update the summary if the job is missing 3300 if alloc.Job == nil { 3301 return nil 3302 } 3303 // COMPAT 0.7: Upgrade old objects that do not have namespaces 3304 if alloc.Namespace == "" { 3305 alloc.Namespace = structs.DefaultNamespace 3306 } 3307 3308 summaryRaw, err := txn.First("job_summary", "id", alloc.Namespace, alloc.JobID) 3309 if err != nil { 3310 return fmt.Errorf("unable to lookup job summary for job id %q in namespace %q: %v", alloc.JobID, alloc.Namespace, err) 3311 } 3312 3313 if summaryRaw == nil { 3314 // Check if the job is de-registered 3315 rawJob, err := txn.First("jobs", "id", alloc.Namespace, alloc.JobID) 3316 if err != nil { 3317 return fmt.Errorf("unable to query job: %v", err) 3318 } 3319 3320 // If the job is de-registered then we skip updating it's summary 3321 if rawJob == nil { 3322 return nil 3323 } 3324 3325 return fmt.Errorf("job summary for job %q in namespace %q is not present", alloc.JobID, alloc.Namespace) 3326 } 3327 3328 // Get a copy of the existing summary 3329 jobSummary := summaryRaw.(*structs.JobSummary).Copy() 3330 3331 // Not updating the job summary because the allocation doesn't belong to the 3332 // currently registered job 3333 if jobSummary.CreateIndex != alloc.Job.CreateIndex { 3334 return nil 3335 } 3336 3337 tgSummary, ok := jobSummary.Summary[alloc.TaskGroup] 3338 if !ok { 3339 return fmt.Errorf("unable to find task group in the job summary: %v", alloc.TaskGroup) 3340 } 3341 3342 summaryChanged := false 3343 if existingAlloc == nil { 3344 switch alloc.DesiredStatus { 3345 case structs.AllocDesiredStatusStop, structs.AllocDesiredStatusEvict: 3346 s.logger.Printf("[ERR] state_store: new allocation inserted into state store with id: %v and state: %v", 3347 alloc.ID, alloc.DesiredStatus) 3348 } 3349 switch alloc.ClientStatus { 3350 case structs.AllocClientStatusPending: 3351 tgSummary.Starting += 1 3352 if tgSummary.Queued > 0 { 3353 tgSummary.Queued -= 1 3354 } 3355 summaryChanged = true 3356 case structs.AllocClientStatusRunning, structs.AllocClientStatusFailed, 3357 structs.AllocClientStatusComplete: 3358 s.logger.Printf("[ERR] state_store: new allocation inserted into state store with id: %v and state: %v", 3359 alloc.ID, alloc.ClientStatus) 3360 } 3361 } else if existingAlloc.ClientStatus != alloc.ClientStatus { 3362 // Incrementing the client of the bin of the current state 3363 switch alloc.ClientStatus { 3364 case structs.AllocClientStatusRunning: 3365 tgSummary.Running += 1 3366 case structs.AllocClientStatusFailed: 3367 tgSummary.Failed += 1 3368 case structs.AllocClientStatusPending: 3369 tgSummary.Starting += 1 3370 case structs.AllocClientStatusComplete: 3371 tgSummary.Complete += 1 3372 case structs.AllocClientStatusLost: 3373 tgSummary.Lost += 1 3374 } 3375 3376 // Decrementing the count of the bin of the last state 3377 switch existingAlloc.ClientStatus { 3378 case structs.AllocClientStatusRunning: 3379 tgSummary.Running -= 1 3380 case structs.AllocClientStatusPending: 3381 tgSummary.Starting -= 1 3382 case structs.AllocClientStatusLost: 3383 tgSummary.Lost -= 1 3384 case structs.AllocClientStatusFailed, structs.AllocClientStatusComplete: 3385 default: 3386 s.logger.Printf("[ERR] state_store: invalid old state of allocation with id: %v, and state: %v", 3387 existingAlloc.ID, existingAlloc.ClientStatus) 3388 } 3389 summaryChanged = true 3390 } 3391 jobSummary.Summary[alloc.TaskGroup] = tgSummary 3392 3393 if summaryChanged { 3394 jobSummary.ModifyIndex = index 3395 3396 // COMPAT 0.7: Upgrade old objects that do not have namespaces 3397 if jobSummary.Namespace == "" { 3398 jobSummary.Namespace = structs.DefaultNamespace 3399 } 3400 3401 // Update the indexes table for job summary 3402 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 3403 return fmt.Errorf("index update failed: %v", err) 3404 } 3405 3406 if err := txn.Insert("job_summary", jobSummary); err != nil { 3407 return fmt.Errorf("updating job summary failed: %v", err) 3408 } 3409 } 3410 3411 return nil 3412 } 3413 3414 // addEphemeralDiskToTaskGroups adds missing EphemeralDisk objects to TaskGroups 3415 func (s *StateStore) addEphemeralDiskToTaskGroups(job *structs.Job) { 3416 for _, tg := range job.TaskGroups { 3417 var diskMB int 3418 for _, task := range tg.Tasks { 3419 if task.Resources != nil { 3420 diskMB += task.Resources.DiskMB 3421 task.Resources.DiskMB = 0 3422 } 3423 } 3424 if tg.EphemeralDisk != nil { 3425 continue 3426 } 3427 tg.EphemeralDisk = &structs.EphemeralDisk{ 3428 SizeMB: diskMB, 3429 } 3430 } 3431 } 3432 3433 // UpsertACLPolicies is used to create or update a set of ACL policies 3434 func (s *StateStore) UpsertACLPolicies(index uint64, policies []*structs.ACLPolicy) error { 3435 txn := s.db.Txn(true) 3436 defer txn.Abort() 3437 3438 for _, policy := range policies { 3439 // Ensure the policy hash is non-nil. This should be done outside the state store 3440 // for performance reasons, but we check here for defense in depth. 3441 if len(policy.Hash) == 0 { 3442 policy.SetHash() 3443 } 3444 3445 // Check if the policy already exists 3446 existing, err := txn.First("acl_policy", "id", policy.Name) 3447 if err != nil { 3448 return fmt.Errorf("policy lookup failed: %v", err) 3449 } 3450 3451 // Update all the indexes 3452 if existing != nil { 3453 policy.CreateIndex = existing.(*structs.ACLPolicy).CreateIndex 3454 policy.ModifyIndex = index 3455 } else { 3456 policy.CreateIndex = index 3457 policy.ModifyIndex = index 3458 } 3459 3460 // Update the policy 3461 if err := txn.Insert("acl_policy", policy); err != nil { 3462 return fmt.Errorf("upserting policy failed: %v", err) 3463 } 3464 } 3465 3466 // Update the indexes tabl 3467 if err := txn.Insert("index", &IndexEntry{"acl_policy", index}); err != nil { 3468 return fmt.Errorf("index update failed: %v", err) 3469 } 3470 3471 txn.Commit() 3472 return nil 3473 } 3474 3475 // DeleteACLPolicies deletes the policies with the given names 3476 func (s *StateStore) DeleteACLPolicies(index uint64, names []string) error { 3477 txn := s.db.Txn(true) 3478 defer txn.Abort() 3479 3480 // Delete the policy 3481 for _, name := range names { 3482 if _, err := txn.DeleteAll("acl_policy", "id", name); err != nil { 3483 return fmt.Errorf("deleting acl policy failed: %v", err) 3484 } 3485 } 3486 if err := txn.Insert("index", &IndexEntry{"acl_policy", index}); err != nil { 3487 return fmt.Errorf("index update failed: %v", err) 3488 } 3489 txn.Commit() 3490 return nil 3491 } 3492 3493 // ACLPolicyByName is used to lookup a policy by name 3494 func (s *StateStore) ACLPolicyByName(ws memdb.WatchSet, name string) (*structs.ACLPolicy, error) { 3495 txn := s.db.Txn(false) 3496 3497 watchCh, existing, err := txn.FirstWatch("acl_policy", "id", name) 3498 if err != nil { 3499 return nil, fmt.Errorf("acl policy lookup failed: %v", err) 3500 } 3501 ws.Add(watchCh) 3502 3503 if existing != nil { 3504 return existing.(*structs.ACLPolicy), nil 3505 } 3506 return nil, nil 3507 } 3508 3509 // ACLPolicyByNamePrefix is used to lookup policies by prefix 3510 func (s *StateStore) ACLPolicyByNamePrefix(ws memdb.WatchSet, prefix string) (memdb.ResultIterator, error) { 3511 txn := s.db.Txn(false) 3512 3513 iter, err := txn.Get("acl_policy", "id_prefix", prefix) 3514 if err != nil { 3515 return nil, fmt.Errorf("acl policy lookup failed: %v", err) 3516 } 3517 ws.Add(iter.WatchCh()) 3518 3519 return iter, nil 3520 } 3521 3522 // ACLPolicies returns an iterator over all the acl policies 3523 func (s *StateStore) ACLPolicies(ws memdb.WatchSet) (memdb.ResultIterator, error) { 3524 txn := s.db.Txn(false) 3525 3526 // Walk the entire table 3527 iter, err := txn.Get("acl_policy", "id") 3528 if err != nil { 3529 return nil, err 3530 } 3531 ws.Add(iter.WatchCh()) 3532 return iter, nil 3533 } 3534 3535 // UpsertACLTokens is used to create or update a set of ACL tokens 3536 func (s *StateStore) UpsertACLTokens(index uint64, tokens []*structs.ACLToken) error { 3537 txn := s.db.Txn(true) 3538 defer txn.Abort() 3539 3540 for _, token := range tokens { 3541 // Ensure the policy hash is non-nil. This should be done outside the state store 3542 // for performance reasons, but we check here for defense in depth. 3543 if len(token.Hash) == 0 { 3544 token.SetHash() 3545 } 3546 3547 // Check if the token already exists 3548 existing, err := txn.First("acl_token", "id", token.AccessorID) 3549 if err != nil { 3550 return fmt.Errorf("token lookup failed: %v", err) 3551 } 3552 3553 // Update all the indexes 3554 if existing != nil { 3555 existTK := existing.(*structs.ACLToken) 3556 token.CreateIndex = existTK.CreateIndex 3557 token.ModifyIndex = index 3558 3559 // Do not allow SecretID or create time to change 3560 token.SecretID = existTK.SecretID 3561 token.CreateTime = existTK.CreateTime 3562 3563 } else { 3564 token.CreateIndex = index 3565 token.ModifyIndex = index 3566 } 3567 3568 // Update the token 3569 if err := txn.Insert("acl_token", token); err != nil { 3570 return fmt.Errorf("upserting token failed: %v", err) 3571 } 3572 } 3573 3574 // Update the indexes table 3575 if err := txn.Insert("index", &IndexEntry{"acl_token", index}); err != nil { 3576 return fmt.Errorf("index update failed: %v", err) 3577 } 3578 txn.Commit() 3579 return nil 3580 } 3581 3582 // DeleteACLTokens deletes the tokens with the given accessor ids 3583 func (s *StateStore) DeleteACLTokens(index uint64, ids []string) error { 3584 txn := s.db.Txn(true) 3585 defer txn.Abort() 3586 3587 // Delete the tokens 3588 for _, id := range ids { 3589 if _, err := txn.DeleteAll("acl_token", "id", id); err != nil { 3590 return fmt.Errorf("deleting acl token failed: %v", err) 3591 } 3592 } 3593 if err := txn.Insert("index", &IndexEntry{"acl_token", index}); err != nil { 3594 return fmt.Errorf("index update failed: %v", err) 3595 } 3596 txn.Commit() 3597 return nil 3598 } 3599 3600 // ACLTokenByAccessorID is used to lookup a token by accessor ID 3601 func (s *StateStore) ACLTokenByAccessorID(ws memdb.WatchSet, id string) (*structs.ACLToken, error) { 3602 txn := s.db.Txn(false) 3603 3604 watchCh, existing, err := txn.FirstWatch("acl_token", "id", id) 3605 if err != nil { 3606 return nil, fmt.Errorf("acl token lookup failed: %v", err) 3607 } 3608 ws.Add(watchCh) 3609 3610 if existing != nil { 3611 return existing.(*structs.ACLToken), nil 3612 } 3613 return nil, nil 3614 } 3615 3616 // ACLTokenBySecretID is used to lookup a token by secret ID 3617 func (s *StateStore) ACLTokenBySecretID(ws memdb.WatchSet, secretID string) (*structs.ACLToken, error) { 3618 txn := s.db.Txn(false) 3619 3620 watchCh, existing, err := txn.FirstWatch("acl_token", "secret", secretID) 3621 if err != nil { 3622 return nil, fmt.Errorf("acl token lookup failed: %v", err) 3623 } 3624 ws.Add(watchCh) 3625 3626 if existing != nil { 3627 return existing.(*structs.ACLToken), nil 3628 } 3629 return nil, nil 3630 } 3631 3632 // ACLTokenByAccessorIDPrefix is used to lookup tokens by prefix 3633 func (s *StateStore) ACLTokenByAccessorIDPrefix(ws memdb.WatchSet, prefix string) (memdb.ResultIterator, error) { 3634 txn := s.db.Txn(false) 3635 3636 iter, err := txn.Get("acl_token", "id_prefix", prefix) 3637 if err != nil { 3638 return nil, fmt.Errorf("acl token lookup failed: %v", err) 3639 } 3640 ws.Add(iter.WatchCh()) 3641 return iter, nil 3642 } 3643 3644 // ACLTokens returns an iterator over all the tokens 3645 func (s *StateStore) ACLTokens(ws memdb.WatchSet) (memdb.ResultIterator, error) { 3646 txn := s.db.Txn(false) 3647 3648 // Walk the entire table 3649 iter, err := txn.Get("acl_token", "id") 3650 if err != nil { 3651 return nil, err 3652 } 3653 ws.Add(iter.WatchCh()) 3654 return iter, nil 3655 } 3656 3657 // ACLTokensByGlobal returns an iterator over all the tokens filtered by global value 3658 func (s *StateStore) ACLTokensByGlobal(ws memdb.WatchSet, globalVal bool) (memdb.ResultIterator, error) { 3659 txn := s.db.Txn(false) 3660 3661 // Walk the entire table 3662 iter, err := txn.Get("acl_token", "global", globalVal) 3663 if err != nil { 3664 return nil, err 3665 } 3666 ws.Add(iter.WatchCh()) 3667 return iter, nil 3668 } 3669 3670 // CanBootstrapACLToken checks if bootstrapping is possible and returns the reset index 3671 func (s *StateStore) CanBootstrapACLToken() (bool, uint64, error) { 3672 txn := s.db.Txn(false) 3673 3674 // Lookup the bootstrap sentinel 3675 out, err := txn.First("index", "id", "acl_token_bootstrap") 3676 if err != nil { 3677 return false, 0, err 3678 } 3679 3680 // No entry, we haven't bootstrapped yet 3681 if out == nil { 3682 return true, 0, nil 3683 } 3684 3685 // Return the reset index if we've already bootstrapped 3686 return false, out.(*IndexEntry).Value, nil 3687 } 3688 3689 // BootstrapACLToken is used to create an initial ACL token 3690 func (s *StateStore) BootstrapACLTokens(index, resetIndex uint64, token *structs.ACLToken) error { 3691 txn := s.db.Txn(true) 3692 defer txn.Abort() 3693 3694 // Check if we have already done a bootstrap 3695 existing, err := txn.First("index", "id", "acl_token_bootstrap") 3696 if err != nil { 3697 return fmt.Errorf("bootstrap check failed: %v", err) 3698 } 3699 if existing != nil { 3700 if resetIndex == 0 { 3701 return fmt.Errorf("ACL bootstrap already done") 3702 } else if resetIndex != existing.(*IndexEntry).Value { 3703 return fmt.Errorf("Invalid reset index for ACL bootstrap") 3704 } 3705 } 3706 3707 // Update the Create/Modify time 3708 token.CreateIndex = index 3709 token.ModifyIndex = index 3710 3711 // Insert the token 3712 if err := txn.Insert("acl_token", token); err != nil { 3713 return fmt.Errorf("upserting token failed: %v", err) 3714 } 3715 3716 // Update the indexes table, prevents future bootstrap until reset 3717 if err := txn.Insert("index", &IndexEntry{"acl_token", index}); err != nil { 3718 return fmt.Errorf("index update failed: %v", err) 3719 } 3720 if err := txn.Insert("index", &IndexEntry{"acl_token_bootstrap", index}); err != nil { 3721 return fmt.Errorf("index update failed: %v", err) 3722 } 3723 txn.Commit() 3724 return nil 3725 } 3726 3727 // StateSnapshot is used to provide a point-in-time snapshot 3728 type StateSnapshot struct { 3729 StateStore 3730 } 3731 3732 // StateRestore is used to optimize the performance when 3733 // restoring state by only using a single large transaction 3734 // instead of thousands of sub transactions 3735 type StateRestore struct { 3736 txn *memdb.Txn 3737 } 3738 3739 // Abort is used to abort the restore operation 3740 func (s *StateRestore) Abort() { 3741 s.txn.Abort() 3742 } 3743 3744 // Commit is used to commit the restore operation 3745 func (s *StateRestore) Commit() { 3746 s.txn.Commit() 3747 } 3748 3749 // NodeRestore is used to restore a node 3750 func (r *StateRestore) NodeRestore(node *structs.Node) error { 3751 if err := r.txn.Insert("nodes", node); err != nil { 3752 return fmt.Errorf("node insert failed: %v", err) 3753 } 3754 return nil 3755 } 3756 3757 // JobRestore is used to restore a job 3758 func (r *StateRestore) JobRestore(job *structs.Job) error { 3759 // Create the EphemeralDisk if it's nil by adding up DiskMB from task resources. 3760 // COMPAT 0.4.1 -> 0.5 3761 r.addEphemeralDiskToTaskGroups(job) 3762 3763 if err := r.txn.Insert("jobs", job); err != nil { 3764 return fmt.Errorf("job insert failed: %v", err) 3765 } 3766 return nil 3767 } 3768 3769 // EvalRestore is used to restore an evaluation 3770 func (r *StateRestore) EvalRestore(eval *structs.Evaluation) error { 3771 if err := r.txn.Insert("evals", eval); err != nil { 3772 return fmt.Errorf("eval insert failed: %v", err) 3773 } 3774 return nil 3775 } 3776 3777 // AllocRestore is used to restore an allocation 3778 func (r *StateRestore) AllocRestore(alloc *structs.Allocation) error { 3779 // Set the shared resources if it's not present 3780 // COMPAT 0.4.1 -> 0.5 3781 if alloc.SharedResources == nil { 3782 alloc.SharedResources = &structs.Resources{ 3783 DiskMB: alloc.Resources.DiskMB, 3784 } 3785 } 3786 3787 // Create the EphemeralDisk if it's nil by adding up DiskMB from task resources. 3788 if alloc.Job != nil { 3789 r.addEphemeralDiskToTaskGroups(alloc.Job) 3790 } 3791 3792 if err := r.txn.Insert("allocs", alloc); err != nil { 3793 return fmt.Errorf("alloc insert failed: %v", err) 3794 } 3795 return nil 3796 } 3797 3798 // IndexRestore is used to restore an index 3799 func (r *StateRestore) IndexRestore(idx *IndexEntry) error { 3800 if err := r.txn.Insert("index", idx); err != nil { 3801 return fmt.Errorf("index insert failed: %v", err) 3802 } 3803 return nil 3804 } 3805 3806 // PeriodicLaunchRestore is used to restore a periodic launch. 3807 func (r *StateRestore) PeriodicLaunchRestore(launch *structs.PeriodicLaunch) error { 3808 if err := r.txn.Insert("periodic_launch", launch); err != nil { 3809 return fmt.Errorf("periodic launch insert failed: %v", err) 3810 } 3811 return nil 3812 } 3813 3814 // JobSummaryRestore is used to restore a job summary 3815 func (r *StateRestore) JobSummaryRestore(jobSummary *structs.JobSummary) error { 3816 if err := r.txn.Insert("job_summary", jobSummary); err != nil { 3817 return fmt.Errorf("job summary insert failed: %v", err) 3818 } 3819 return nil 3820 } 3821 3822 // JobVersionRestore is used to restore a job version 3823 func (r *StateRestore) JobVersionRestore(version *structs.Job) error { 3824 if err := r.txn.Insert("job_version", version); err != nil { 3825 return fmt.Errorf("job version insert failed: %v", err) 3826 } 3827 return nil 3828 } 3829 3830 // DeploymentRestore is used to restore a deployment 3831 func (r *StateRestore) DeploymentRestore(deployment *structs.Deployment) error { 3832 if err := r.txn.Insert("deployment", deployment); err != nil { 3833 return fmt.Errorf("deployment insert failed: %v", err) 3834 } 3835 return nil 3836 } 3837 3838 // VaultAccessorRestore is used to restore a vault accessor 3839 func (r *StateRestore) VaultAccessorRestore(accessor *structs.VaultAccessor) error { 3840 if err := r.txn.Insert("vault_accessors", accessor); err != nil { 3841 return fmt.Errorf("vault accessor insert failed: %v", err) 3842 } 3843 return nil 3844 } 3845 3846 // ACLPolicyRestore is used to restore an ACL policy 3847 func (r *StateRestore) ACLPolicyRestore(policy *structs.ACLPolicy) error { 3848 if err := r.txn.Insert("acl_policy", policy); err != nil { 3849 return fmt.Errorf("inserting acl policy failed: %v", err) 3850 } 3851 return nil 3852 } 3853 3854 // ACLTokenRestore is used to restore an ACL token 3855 func (r *StateRestore) ACLTokenRestore(token *structs.ACLToken) error { 3856 if err := r.txn.Insert("acl_token", token); err != nil { 3857 return fmt.Errorf("inserting acl token failed: %v", err) 3858 } 3859 return nil 3860 } 3861 3862 // addEphemeralDiskToTaskGroups adds missing EphemeralDisk objects to TaskGroups 3863 func (r *StateRestore) addEphemeralDiskToTaskGroups(job *structs.Job) { 3864 for _, tg := range job.TaskGroups { 3865 if tg.EphemeralDisk != nil { 3866 continue 3867 } 3868 var sizeMB int 3869 for _, task := range tg.Tasks { 3870 if task.Resources != nil { 3871 sizeMB += task.Resources.DiskMB 3872 task.Resources.DiskMB = 0 3873 } 3874 } 3875 tg.EphemeralDisk = &structs.EphemeralDisk{ 3876 SizeMB: sizeMB, 3877 } 3878 } 3879 }