github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/nomad/state/schema.go (about) 1 package state 2 3 import ( 4 "fmt" 5 "sync" 6 7 memdb "github.com/hashicorp/go-memdb" 8 9 "github.com/hashicorp/nomad/nomad/structs" 10 ) 11 12 const ( 13 TableNamespaces = "namespaces" 14 ) 15 16 var ( 17 schemaFactories SchemaFactories 18 factoriesLock sync.Mutex 19 ) 20 21 // SchemaFactory is the factory method for returning a TableSchema 22 type SchemaFactory func() *memdb.TableSchema 23 type SchemaFactories []SchemaFactory 24 25 // RegisterSchemaFactories is used to register a table schema. 26 func RegisterSchemaFactories(factories ...SchemaFactory) { 27 factoriesLock.Lock() 28 defer factoriesLock.Unlock() 29 schemaFactories = append(schemaFactories, factories...) 30 } 31 32 func GetFactories() SchemaFactories { 33 return schemaFactories 34 } 35 36 func init() { 37 // Register all schemas 38 RegisterSchemaFactories([]SchemaFactory{ 39 indexTableSchema, 40 nodeTableSchema, 41 jobTableSchema, 42 jobSummarySchema, 43 jobVersionSchema, 44 deploymentSchema, 45 periodicLaunchTableSchema, 46 evalTableSchema, 47 allocTableSchema, 48 vaultAccessorTableSchema, 49 siTokenAccessorTableSchema, 50 aclPolicyTableSchema, 51 aclTokenTableSchema, 52 autopilotConfigTableSchema, 53 schedulerConfigTableSchema, 54 clusterMetaTableSchema, 55 csiVolumeTableSchema, 56 csiPluginTableSchema, 57 scalingPolicyTableSchema, 58 scalingEventTableSchema, 59 namespaceTableSchema, 60 }...) 61 } 62 63 // stateStoreSchema is used to return the schema for the state store 64 func stateStoreSchema() *memdb.DBSchema { 65 // Create the root DB schema 66 db := &memdb.DBSchema{ 67 Tables: make(map[string]*memdb.TableSchema), 68 } 69 70 // Add each of the tables 71 for _, schemaFn := range GetFactories() { 72 schema := schemaFn() 73 if _, ok := db.Tables[schema.Name]; ok { 74 panic(fmt.Sprintf("duplicate table name: %s", schema.Name)) 75 } 76 db.Tables[schema.Name] = schema 77 } 78 return db 79 } 80 81 // indexTableSchema is used for tracking the most recent index used for each table. 82 func indexTableSchema() *memdb.TableSchema { 83 return &memdb.TableSchema{ 84 Name: "index", 85 Indexes: map[string]*memdb.IndexSchema{ 86 "id": { 87 Name: "id", 88 AllowMissing: false, 89 Unique: true, 90 Indexer: &memdb.StringFieldIndex{ 91 Field: "Key", 92 Lowercase: true, 93 }, 94 }, 95 }, 96 } 97 } 98 99 // nodeTableSchema returns the MemDB schema for the nodes table. 100 // This table is used to store all the client nodes that are registered. 101 func nodeTableSchema() *memdb.TableSchema { 102 return &memdb.TableSchema{ 103 Name: "nodes", 104 Indexes: map[string]*memdb.IndexSchema{ 105 // Primary index is used for node management 106 // and simple direct lookup. ID is required to be 107 // unique. 108 "id": { 109 Name: "id", 110 AllowMissing: false, 111 Unique: true, 112 Indexer: &memdb.UUIDFieldIndex{ 113 Field: "ID", 114 }, 115 }, 116 "secret_id": { 117 Name: "secret_id", 118 AllowMissing: false, 119 Unique: true, 120 Indexer: &memdb.UUIDFieldIndex{ 121 Field: "SecretID", 122 }, 123 }, 124 }, 125 } 126 } 127 128 // jobTableSchema returns the MemDB schema for the jobs table. 129 // This table is used to store all the jobs that have been submitted. 130 func jobTableSchema() *memdb.TableSchema { 131 return &memdb.TableSchema{ 132 Name: "jobs", 133 Indexes: map[string]*memdb.IndexSchema{ 134 // Primary index is used for job management 135 // and simple direct lookup. ID is required to be 136 // unique within a namespace. 137 "id": { 138 Name: "id", 139 AllowMissing: false, 140 Unique: true, 141 142 // Use a compound index so the tuple of (Namespace, ID) is 143 // uniquely identifying 144 Indexer: &memdb.CompoundIndex{ 145 Indexes: []memdb.Indexer{ 146 &memdb.StringFieldIndex{ 147 Field: "Namespace", 148 }, 149 150 &memdb.StringFieldIndex{ 151 Field: "ID", 152 }, 153 }, 154 }, 155 }, 156 "type": { 157 Name: "type", 158 AllowMissing: false, 159 Unique: false, 160 Indexer: &memdb.StringFieldIndex{ 161 Field: "Type", 162 Lowercase: false, 163 }, 164 }, 165 "gc": { 166 Name: "gc", 167 AllowMissing: false, 168 Unique: false, 169 Indexer: &memdb.ConditionalIndex{ 170 Conditional: jobIsGCable, 171 }, 172 }, 173 "periodic": { 174 Name: "periodic", 175 AllowMissing: false, 176 Unique: false, 177 Indexer: &memdb.ConditionalIndex{ 178 Conditional: jobIsPeriodic, 179 }, 180 }, 181 }, 182 } 183 } 184 185 // jobSummarySchema returns the memdb schema for the job summary table 186 func jobSummarySchema() *memdb.TableSchema { 187 return &memdb.TableSchema{ 188 Name: "job_summary", 189 Indexes: map[string]*memdb.IndexSchema{ 190 "id": { 191 Name: "id", 192 AllowMissing: false, 193 Unique: true, 194 195 // Use a compound index so the tuple of (Namespace, JobID) is 196 // uniquely identifying 197 Indexer: &memdb.CompoundIndex{ 198 Indexes: []memdb.Indexer{ 199 &memdb.StringFieldIndex{ 200 Field: "Namespace", 201 }, 202 203 &memdb.StringFieldIndex{ 204 Field: "JobID", 205 }, 206 }, 207 }, 208 }, 209 }, 210 } 211 } 212 213 // jobVersionSchema returns the memdb schema for the job version table which 214 // keeps a historical view of job versions. 215 func jobVersionSchema() *memdb.TableSchema { 216 return &memdb.TableSchema{ 217 Name: "job_version", 218 Indexes: map[string]*memdb.IndexSchema{ 219 "id": { 220 Name: "id", 221 AllowMissing: false, 222 Unique: true, 223 224 // Use a compound index so the tuple of (Namespace, ID, Version) is 225 // uniquely identifying 226 Indexer: &memdb.CompoundIndex{ 227 Indexes: []memdb.Indexer{ 228 &memdb.StringFieldIndex{ 229 Field: "Namespace", 230 }, 231 232 &memdb.StringFieldIndex{ 233 Field: "ID", 234 Lowercase: true, 235 }, 236 237 &memdb.UintFieldIndex{ 238 Field: "Version", 239 }, 240 }, 241 }, 242 }, 243 }, 244 } 245 } 246 247 // jobIsGCable satisfies the ConditionalIndexFunc interface and creates an index 248 // on whether a job is eligible for garbage collection. 249 func jobIsGCable(obj interface{}) (bool, error) { 250 j, ok := obj.(*structs.Job) 251 if !ok { 252 return false, fmt.Errorf("Unexpected type: %v", obj) 253 } 254 255 // If the job is periodic or parameterized it is only garbage collectable if 256 // it is stopped. 257 periodic := j.Periodic != nil && j.Periodic.Enabled 258 parameterized := j.IsParameterized() 259 if periodic || parameterized { 260 return j.Stop, nil 261 } 262 263 // If the job isn't dead it isn't eligible 264 if j.Status != structs.JobStatusDead { 265 return false, nil 266 } 267 268 // Any job that is stopped is eligible for garbage collection 269 if j.Stop { 270 return true, nil 271 } 272 273 // Otherwise, only batch jobs are eligible because they complete on their 274 // own without a user stopping them. 275 if j.Type != structs.JobTypeBatch { 276 return false, nil 277 } 278 279 return true, nil 280 } 281 282 // jobIsPeriodic satisfies the ConditionalIndexFunc interface and creates an index 283 // on whether a job is periodic. 284 func jobIsPeriodic(obj interface{}) (bool, error) { 285 j, ok := obj.(*structs.Job) 286 if !ok { 287 return false, fmt.Errorf("Unexpected type: %v", obj) 288 } 289 290 if j.Periodic != nil && j.Periodic.Enabled { 291 return true, nil 292 } 293 294 return false, nil 295 } 296 297 // deploymentSchema returns the MemDB schema tracking a job's deployments 298 func deploymentSchema() *memdb.TableSchema { 299 return &memdb.TableSchema{ 300 Name: "deployment", 301 Indexes: map[string]*memdb.IndexSchema{ 302 "id": { 303 Name: "id", 304 AllowMissing: false, 305 Unique: true, 306 Indexer: &memdb.UUIDFieldIndex{ 307 Field: "ID", 308 }, 309 }, 310 311 "namespace": { 312 Name: "namespace", 313 AllowMissing: false, 314 Unique: false, 315 Indexer: &memdb.StringFieldIndex{ 316 Field: "Namespace", 317 }, 318 }, 319 320 // Job index is used to lookup deployments by job 321 "job": { 322 Name: "job", 323 AllowMissing: false, 324 Unique: false, 325 326 // Use a compound index so the tuple of (Namespace, JobID) is 327 // uniquely identifying 328 Indexer: &memdb.CompoundIndex{ 329 Indexes: []memdb.Indexer{ 330 &memdb.StringFieldIndex{ 331 Field: "Namespace", 332 }, 333 334 &memdb.StringFieldIndex{ 335 Field: "JobID", 336 }, 337 }, 338 }, 339 }, 340 }, 341 } 342 } 343 344 // periodicLaunchTableSchema returns the MemDB schema tracking the most recent 345 // launch time for a periodic job. 346 func periodicLaunchTableSchema() *memdb.TableSchema { 347 return &memdb.TableSchema{ 348 Name: "periodic_launch", 349 Indexes: map[string]*memdb.IndexSchema{ 350 // Primary index is used for job management 351 // and simple direct lookup. ID is required to be 352 // unique. 353 "id": { 354 Name: "id", 355 AllowMissing: false, 356 Unique: true, 357 358 // Use a compound index so the tuple of (Namespace, JobID) is 359 // uniquely identifying 360 Indexer: &memdb.CompoundIndex{ 361 Indexes: []memdb.Indexer{ 362 &memdb.StringFieldIndex{ 363 Field: "Namespace", 364 }, 365 366 &memdb.StringFieldIndex{ 367 Field: "ID", 368 }, 369 }, 370 }, 371 }, 372 }, 373 } 374 } 375 376 // evalTableSchema returns the MemDB schema for the eval table. 377 // This table is used to store all the evaluations that are pending 378 // or recently completed. 379 func evalTableSchema() *memdb.TableSchema { 380 return &memdb.TableSchema{ 381 Name: "evals", 382 Indexes: map[string]*memdb.IndexSchema{ 383 // Primary index is used for direct lookup. 384 "id": { 385 Name: "id", 386 AllowMissing: false, 387 Unique: true, 388 Indexer: &memdb.UUIDFieldIndex{ 389 Field: "ID", 390 }, 391 }, 392 393 "namespace": { 394 Name: "namespace", 395 AllowMissing: false, 396 Unique: false, 397 Indexer: &memdb.StringFieldIndex{ 398 Field: "Namespace", 399 }, 400 }, 401 402 // Job index is used to lookup allocations by job 403 "job": { 404 Name: "job", 405 AllowMissing: false, 406 Unique: false, 407 Indexer: &memdb.CompoundIndex{ 408 Indexes: []memdb.Indexer{ 409 &memdb.StringFieldIndex{ 410 Field: "Namespace", 411 }, 412 413 &memdb.StringFieldIndex{ 414 Field: "JobID", 415 Lowercase: true, 416 }, 417 418 &memdb.StringFieldIndex{ 419 Field: "Status", 420 Lowercase: true, 421 }, 422 }, 423 }, 424 }, 425 }, 426 } 427 } 428 429 // allocTableSchema returns the MemDB schema for the allocation table. 430 // This table is used to store all the task allocations between task groups 431 // and nodes. 432 func allocTableSchema() *memdb.TableSchema { 433 return &memdb.TableSchema{ 434 Name: "allocs", 435 Indexes: map[string]*memdb.IndexSchema{ 436 // Primary index is a UUID 437 "id": { 438 Name: "id", 439 AllowMissing: false, 440 Unique: true, 441 Indexer: &memdb.UUIDFieldIndex{ 442 Field: "ID", 443 }, 444 }, 445 446 "namespace": { 447 Name: "namespace", 448 AllowMissing: false, 449 Unique: false, 450 Indexer: &memdb.StringFieldIndex{ 451 Field: "Namespace", 452 }, 453 }, 454 455 // Node index is used to lookup allocations by node 456 "node": { 457 Name: "node", 458 AllowMissing: true, // Missing is allow for failed allocations 459 Unique: false, 460 Indexer: &memdb.CompoundIndex{ 461 Indexes: []memdb.Indexer{ 462 &memdb.StringFieldIndex{ 463 Field: "NodeID", 464 Lowercase: true, 465 }, 466 467 // Conditional indexer on if allocation is terminal 468 &memdb.ConditionalIndex{ 469 Conditional: func(obj interface{}) (bool, error) { 470 // Cast to allocation 471 alloc, ok := obj.(*structs.Allocation) 472 if !ok { 473 return false, fmt.Errorf("wrong type, got %t should be Allocation", obj) 474 } 475 476 // Check if the allocation is terminal 477 return alloc.TerminalStatus(), nil 478 }, 479 }, 480 }, 481 }, 482 }, 483 484 // Job index is used to lookup allocations by job 485 "job": { 486 Name: "job", 487 AllowMissing: false, 488 Unique: false, 489 490 Indexer: &memdb.CompoundIndex{ 491 Indexes: []memdb.Indexer{ 492 &memdb.StringFieldIndex{ 493 Field: "Namespace", 494 }, 495 496 &memdb.StringFieldIndex{ 497 Field: "JobID", 498 }, 499 }, 500 }, 501 }, 502 503 // Eval index is used to lookup allocations by eval 504 "eval": { 505 Name: "eval", 506 AllowMissing: false, 507 Unique: false, 508 Indexer: &memdb.UUIDFieldIndex{ 509 Field: "EvalID", 510 }, 511 }, 512 513 // Deployment index is used to lookup allocations by deployment 514 "deployment": { 515 Name: "deployment", 516 AllowMissing: true, 517 Unique: false, 518 Indexer: &memdb.UUIDFieldIndex{ 519 Field: "DeploymentID", 520 }, 521 }, 522 }, 523 } 524 } 525 526 // vaultAccessorTableSchema returns the MemDB schema for the Vault Accessor 527 // Table. This table tracks Vault accessors for tokens created on behalf of 528 // allocations required Vault tokens. 529 func vaultAccessorTableSchema() *memdb.TableSchema { 530 return &memdb.TableSchema{ 531 Name: "vault_accessors", 532 Indexes: map[string]*memdb.IndexSchema{ 533 // The primary index is the accessor id 534 "id": { 535 Name: "id", 536 AllowMissing: false, 537 Unique: true, 538 Indexer: &memdb.StringFieldIndex{ 539 Field: "Accessor", 540 }, 541 }, 542 543 "alloc_id": { 544 Name: "alloc_id", 545 AllowMissing: false, 546 Unique: false, 547 Indexer: &memdb.StringFieldIndex{ 548 Field: "AllocID", 549 }, 550 }, 551 552 "node_id": { 553 Name: "node_id", 554 AllowMissing: false, 555 Unique: false, 556 Indexer: &memdb.StringFieldIndex{ 557 Field: "NodeID", 558 }, 559 }, 560 }, 561 } 562 } 563 564 // siTokenAccessorTableSchema returns the MemDB schema for the Service Identity 565 // token accessor table. This table tracks accessors for tokens created on behalf 566 // of allocations with Consul connect enabled tasks that need SI tokens. 567 func siTokenAccessorTableSchema() *memdb.TableSchema { 568 return &memdb.TableSchema{ 569 Name: siTokenAccessorTable, 570 Indexes: map[string]*memdb.IndexSchema{ 571 // The primary index is the accessor id 572 "id": { 573 Name: "id", 574 AllowMissing: false, 575 Unique: true, 576 Indexer: &memdb.StringFieldIndex{ 577 Field: "AccessorID", 578 }, 579 }, 580 581 "alloc_id": { 582 Name: "alloc_id", 583 AllowMissing: false, 584 Unique: false, 585 Indexer: &memdb.StringFieldIndex{ 586 Field: "AllocID", 587 }, 588 }, 589 590 "node_id": { 591 Name: "node_id", 592 AllowMissing: false, 593 Unique: false, 594 Indexer: &memdb.StringFieldIndex{ 595 Field: "NodeID", 596 }, 597 }, 598 }, 599 } 600 } 601 602 // aclPolicyTableSchema returns the MemDB schema for the policy table. 603 // This table is used to store the policies which are referenced by tokens 604 func aclPolicyTableSchema() *memdb.TableSchema { 605 return &memdb.TableSchema{ 606 Name: "acl_policy", 607 Indexes: map[string]*memdb.IndexSchema{ 608 "id": { 609 Name: "id", 610 AllowMissing: false, 611 Unique: true, 612 Indexer: &memdb.StringFieldIndex{ 613 Field: "Name", 614 }, 615 }, 616 }, 617 } 618 } 619 620 // aclTokenTableSchema returns the MemDB schema for the tokens table. 621 // This table is used to store the bearer tokens which are used to authenticate 622 func aclTokenTableSchema() *memdb.TableSchema { 623 return &memdb.TableSchema{ 624 Name: "acl_token", 625 Indexes: map[string]*memdb.IndexSchema{ 626 "id": { 627 Name: "id", 628 AllowMissing: false, 629 Unique: true, 630 Indexer: &memdb.UUIDFieldIndex{ 631 Field: "AccessorID", 632 }, 633 }, 634 "secret": { 635 Name: "secret", 636 AllowMissing: false, 637 Unique: true, 638 Indexer: &memdb.UUIDFieldIndex{ 639 Field: "SecretID", 640 }, 641 }, 642 "global": { 643 Name: "global", 644 AllowMissing: false, 645 Unique: false, 646 Indexer: &memdb.FieldSetIndex{ 647 Field: "Global", 648 }, 649 }, 650 }, 651 } 652 } 653 654 // singletonRecord can be used to describe tables which should contain only 1 entry. 655 // Example uses include storing node config or cluster metadata blobs. 656 var singletonRecord = &memdb.ConditionalIndex{ 657 Conditional: func(interface{}) (bool, error) { return true, nil }, 658 } 659 660 // schedulerConfigTableSchema returns the MemDB schema for the scheduler config table. 661 // This table is used to store configuration options for the scheduler 662 func schedulerConfigTableSchema() *memdb.TableSchema { 663 return &memdb.TableSchema{ 664 Name: "scheduler_config", 665 Indexes: map[string]*memdb.IndexSchema{ 666 "id": { 667 Name: "id", 668 AllowMissing: true, 669 Unique: true, 670 Indexer: singletonRecord, // we store only 1 scheduler config 671 }, 672 }, 673 } 674 } 675 676 // clusterMetaTableSchema returns the MemDB schema for the scheduler config table. 677 func clusterMetaTableSchema() *memdb.TableSchema { 678 return &memdb.TableSchema{ 679 Name: "cluster_meta", 680 Indexes: map[string]*memdb.IndexSchema{ 681 "id": { 682 Name: "id", 683 AllowMissing: false, 684 Unique: true, 685 Indexer: singletonRecord, // we store only 1 cluster metadata 686 }, 687 }, 688 } 689 } 690 691 // CSIVolumes are identified by id globally, and searchable by driver 692 func csiVolumeTableSchema() *memdb.TableSchema { 693 return &memdb.TableSchema{ 694 Name: "csi_volumes", 695 Indexes: map[string]*memdb.IndexSchema{ 696 "id": { 697 Name: "id", 698 AllowMissing: false, 699 Unique: true, 700 Indexer: &memdb.CompoundIndex{ 701 Indexes: []memdb.Indexer{ 702 &memdb.StringFieldIndex{ 703 Field: "Namespace", 704 }, 705 &memdb.StringFieldIndex{ 706 Field: "ID", 707 }, 708 }, 709 }, 710 }, 711 "plugin_id": { 712 Name: "plugin_id", 713 AllowMissing: false, 714 Unique: false, 715 Indexer: &memdb.StringFieldIndex{ 716 Field: "PluginID", 717 }, 718 }, 719 }, 720 } 721 } 722 723 // CSIPlugins are identified by id globally, and searchable by driver 724 func csiPluginTableSchema() *memdb.TableSchema { 725 return &memdb.TableSchema{ 726 Name: "csi_plugins", 727 Indexes: map[string]*memdb.IndexSchema{ 728 "id": { 729 Name: "id", 730 AllowMissing: false, 731 Unique: true, 732 Indexer: &memdb.StringFieldIndex{ 733 Field: "ID", 734 }, 735 }, 736 }, 737 } 738 } 739 740 // StringFieldIndex is used to extract a field from an object 741 // using reflection and builds an index on that field. 742 type ScalingPolicyTargetFieldIndex struct { 743 Field string 744 745 // AllowMissing controls if the field should be ignored if the field is 746 // not provided. 747 AllowMissing bool 748 } 749 750 // FromObject is used to extract an index value from an 751 // object or to indicate that the index value is missing. 752 func (s *ScalingPolicyTargetFieldIndex) FromObject(obj interface{}) (bool, []byte, error) { 753 policy, ok := obj.(*structs.ScalingPolicy) 754 if !ok { 755 return false, nil, fmt.Errorf("object %#v is not a ScalingPolicy", obj) 756 } 757 758 if policy.Target == nil { 759 return false, nil, nil 760 } 761 762 val, ok := policy.Target[s.Field] 763 if !ok && !s.AllowMissing { 764 return false, nil, nil 765 } 766 767 // Add the null character as a terminator 768 val += "\x00" 769 return true, []byte(val), nil 770 } 771 772 // FromArgs is used to build an exact index lookup based on arguments 773 func (s *ScalingPolicyTargetFieldIndex) FromArgs(args ...interface{}) ([]byte, error) { 774 if len(args) != 1 { 775 return nil, fmt.Errorf("must provide only a single argument") 776 } 777 arg, ok := args[0].(string) 778 if !ok { 779 return nil, fmt.Errorf("argument must be a string: %#v", args[0]) 780 } 781 // Add the null character as a terminator 782 arg += "\x00" 783 return []byte(arg), nil 784 } 785 786 // PrefixFromArgs returns a prefix that should be used for scanning based on the arguments 787 func (s *ScalingPolicyTargetFieldIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) { 788 val, err := s.FromArgs(args...) 789 if err != nil { 790 return nil, err 791 } 792 793 // Strip the null terminator, the rest is a prefix 794 n := len(val) 795 if n > 0 { 796 return val[:n-1], nil 797 } 798 return val, nil 799 } 800 801 // scalingPolicyTableSchema returns the MemDB schema for the policy table. 802 func scalingPolicyTableSchema() *memdb.TableSchema { 803 return &memdb.TableSchema{ 804 Name: "scaling_policy", 805 Indexes: map[string]*memdb.IndexSchema{ 806 // Primary index is used for simple direct lookup. 807 "id": { 808 Name: "id", 809 AllowMissing: false, 810 Unique: true, 811 812 // UUID is uniquely identifying 813 Indexer: &memdb.StringFieldIndex{ 814 Field: "ID", 815 }, 816 }, 817 // Target index is used for listing by namespace or job, or looking up a specific target. 818 // A given task group can have only a single scaling policies, so this is guaranteed to be unique. 819 "target": { 820 Name: "target", 821 Unique: false, 822 823 // Use a compound index so the tuple of (Namespace, Job, Group, Task) is 824 // used when looking for a policy 825 Indexer: &memdb.CompoundIndex{ 826 Indexes: []memdb.Indexer{ 827 &ScalingPolicyTargetFieldIndex{ 828 Field: "Namespace", 829 AllowMissing: true, 830 }, 831 832 &ScalingPolicyTargetFieldIndex{ 833 Field: "Job", 834 AllowMissing: true, 835 }, 836 837 &ScalingPolicyTargetFieldIndex{ 838 Field: "Group", 839 AllowMissing: true, 840 }, 841 842 &ScalingPolicyTargetFieldIndex{ 843 Field: "Task", 844 AllowMissing: true, 845 }, 846 }, 847 }, 848 }, 849 // Type index is used for listing by policy type 850 "type": { 851 Name: "type", 852 AllowMissing: false, 853 Unique: false, 854 Indexer: &memdb.StringFieldIndex{ 855 Field: "Type", 856 }, 857 }, 858 // Used to filter by enabled 859 "enabled": { 860 Name: "enabled", 861 AllowMissing: false, 862 Unique: false, 863 Indexer: &memdb.FieldSetIndex{ 864 Field: "Enabled", 865 }, 866 }, 867 }, 868 } 869 } 870 871 // scalingEventTableSchema returns the memdb schema for job scaling events 872 func scalingEventTableSchema() *memdb.TableSchema { 873 return &memdb.TableSchema{ 874 Name: "scaling_event", 875 Indexes: map[string]*memdb.IndexSchema{ 876 "id": { 877 Name: "id", 878 AllowMissing: false, 879 Unique: true, 880 881 // Use a compound index so the tuple of (Namespace, JobID) is 882 // uniquely identifying 883 Indexer: &memdb.CompoundIndex{ 884 Indexes: []memdb.Indexer{ 885 &memdb.StringFieldIndex{ 886 Field: "Namespace", 887 }, 888 889 &memdb.StringFieldIndex{ 890 Field: "JobID", 891 }, 892 }, 893 }, 894 }, 895 896 // TODO: need to figure out whether we want to index these or the jobs or ... 897 // "error": { 898 // Name: "error", 899 // AllowMissing: false, 900 // Unique: false, 901 // Indexer: &memdb.FieldSetIndex{ 902 // Field: "Error", 903 // }, 904 // }, 905 }, 906 } 907 } 908 909 // namespaceTableSchema returns the MemDB schema for the namespace table. 910 func namespaceTableSchema() *memdb.TableSchema { 911 return &memdb.TableSchema{ 912 Name: TableNamespaces, 913 Indexes: map[string]*memdb.IndexSchema{ 914 "id": { 915 Name: "id", 916 AllowMissing: false, 917 Unique: true, 918 Indexer: &memdb.StringFieldIndex{ 919 Field: "Name", 920 }, 921 }, 922 "quota": { 923 Name: "quota", 924 AllowMissing: true, 925 Unique: false, 926 Indexer: &memdb.StringFieldIndex{ 927 Field: "Quota", 928 }, 929 }, 930 }, 931 } 932 }