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