github.com/manicqin/nomad@v0.9.5/nomad/state/schema.go (about) 1 package state 2 3 import ( 4 "fmt" 5 "sync" 6 7 memdb "github.com/hashicorp/go-memdb" 8 "github.com/hashicorp/nomad/nomad/structs" 9 ) 10 11 var ( 12 schemaFactories SchemaFactories 13 factoriesLock sync.Mutex 14 ) 15 16 // SchemaFactory is the factory method for returning a TableSchema 17 type SchemaFactory func() *memdb.TableSchema 18 type SchemaFactories []SchemaFactory 19 20 // RegisterSchemaFactories is used to register a table schema. 21 func RegisterSchemaFactories(factories ...SchemaFactory) { 22 factoriesLock.Lock() 23 defer factoriesLock.Unlock() 24 schemaFactories = append(schemaFactories, factories...) 25 } 26 27 func GetFactories() SchemaFactories { 28 return schemaFactories 29 } 30 31 func init() { 32 // Register all schemas 33 RegisterSchemaFactories([]SchemaFactory{ 34 indexTableSchema, 35 nodeTableSchema, 36 jobTableSchema, 37 jobSummarySchema, 38 jobVersionSchema, 39 deploymentSchema, 40 periodicLaunchTableSchema, 41 evalTableSchema, 42 allocTableSchema, 43 vaultAccessorTableSchema, 44 aclPolicyTableSchema, 45 aclTokenTableSchema, 46 autopilotConfigTableSchema, 47 schedulerConfigTableSchema, 48 }...) 49 } 50 51 // stateStoreSchema is used to return the schema for the state store 52 func stateStoreSchema() *memdb.DBSchema { 53 // Create the root DB schema 54 db := &memdb.DBSchema{ 55 Tables: make(map[string]*memdb.TableSchema), 56 } 57 58 // Add each of the tables 59 for _, schemaFn := range GetFactories() { 60 schema := schemaFn() 61 if _, ok := db.Tables[schema.Name]; ok { 62 panic(fmt.Sprintf("duplicate table name: %s", schema.Name)) 63 } 64 db.Tables[schema.Name] = schema 65 } 66 return db 67 } 68 69 // indexTableSchema is used for 70 func indexTableSchema() *memdb.TableSchema { 71 return &memdb.TableSchema{ 72 Name: "index", 73 Indexes: map[string]*memdb.IndexSchema{ 74 "id": { 75 Name: "id", 76 AllowMissing: false, 77 Unique: true, 78 Indexer: &memdb.StringFieldIndex{ 79 Field: "Key", 80 Lowercase: true, 81 }, 82 }, 83 }, 84 } 85 } 86 87 // nodeTableSchema returns the MemDB schema for the nodes table. 88 // This table is used to store all the client nodes that are registered. 89 func nodeTableSchema() *memdb.TableSchema { 90 return &memdb.TableSchema{ 91 Name: "nodes", 92 Indexes: map[string]*memdb.IndexSchema{ 93 // Primary index is used for node management 94 // and simple direct lookup. ID is required to be 95 // unique. 96 "id": { 97 Name: "id", 98 AllowMissing: false, 99 Unique: true, 100 Indexer: &memdb.UUIDFieldIndex{ 101 Field: "ID", 102 }, 103 }, 104 "secret_id": { 105 Name: "secret_id", 106 AllowMissing: false, 107 Unique: true, 108 Indexer: &memdb.UUIDFieldIndex{ 109 Field: "SecretID", 110 }, 111 }, 112 }, 113 } 114 } 115 116 // jobTableSchema returns the MemDB schema for the jobs table. 117 // This table is used to store all the jobs that have been submitted. 118 func jobTableSchema() *memdb.TableSchema { 119 return &memdb.TableSchema{ 120 Name: "jobs", 121 Indexes: map[string]*memdb.IndexSchema{ 122 // Primary index is used for job management 123 // and simple direct lookup. ID is required to be 124 // unique within a namespace. 125 "id": { 126 Name: "id", 127 AllowMissing: false, 128 Unique: true, 129 130 // Use a compound index so the tuple of (Namespace, ID) is 131 // uniquely identifying 132 Indexer: &memdb.CompoundIndex{ 133 Indexes: []memdb.Indexer{ 134 &memdb.StringFieldIndex{ 135 Field: "Namespace", 136 }, 137 138 &memdb.StringFieldIndex{ 139 Field: "ID", 140 }, 141 }, 142 }, 143 }, 144 "type": { 145 Name: "type", 146 AllowMissing: false, 147 Unique: false, 148 Indexer: &memdb.StringFieldIndex{ 149 Field: "Type", 150 Lowercase: false, 151 }, 152 }, 153 "gc": { 154 Name: "gc", 155 AllowMissing: false, 156 Unique: false, 157 Indexer: &memdb.ConditionalIndex{ 158 Conditional: jobIsGCable, 159 }, 160 }, 161 "periodic": { 162 Name: "periodic", 163 AllowMissing: false, 164 Unique: false, 165 Indexer: &memdb.ConditionalIndex{ 166 Conditional: jobIsPeriodic, 167 }, 168 }, 169 }, 170 } 171 } 172 173 // jobSummarySchema returns the memdb schema for the job summary table 174 func jobSummarySchema() *memdb.TableSchema { 175 return &memdb.TableSchema{ 176 Name: "job_summary", 177 Indexes: map[string]*memdb.IndexSchema{ 178 "id": { 179 Name: "id", 180 AllowMissing: false, 181 Unique: true, 182 183 // Use a compound index so the tuple of (Namespace, JobID) is 184 // uniquely identifying 185 Indexer: &memdb.CompoundIndex{ 186 Indexes: []memdb.Indexer{ 187 &memdb.StringFieldIndex{ 188 Field: "Namespace", 189 }, 190 191 &memdb.StringFieldIndex{ 192 Field: "JobID", 193 }, 194 }, 195 }, 196 }, 197 }, 198 } 199 } 200 201 // jobVersionSchema returns the memdb schema for the job version table which 202 // keeps a historical view of job versions. 203 func jobVersionSchema() *memdb.TableSchema { 204 return &memdb.TableSchema{ 205 Name: "job_version", 206 Indexes: map[string]*memdb.IndexSchema{ 207 "id": { 208 Name: "id", 209 AllowMissing: false, 210 Unique: true, 211 212 // Use a compound index so the tuple of (Namespace, ID, Version) is 213 // uniquely identifying 214 Indexer: &memdb.CompoundIndex{ 215 Indexes: []memdb.Indexer{ 216 &memdb.StringFieldIndex{ 217 Field: "Namespace", 218 }, 219 220 &memdb.StringFieldIndex{ 221 Field: "ID", 222 Lowercase: true, 223 }, 224 225 &memdb.UintFieldIndex{ 226 Field: "Version", 227 }, 228 }, 229 }, 230 }, 231 }, 232 } 233 } 234 235 // jobIsGCable satisfies the ConditionalIndexFunc interface and creates an index 236 // on whether a job is eligible for garbage collection. 237 func jobIsGCable(obj interface{}) (bool, error) { 238 j, ok := obj.(*structs.Job) 239 if !ok { 240 return false, fmt.Errorf("Unexpected type: %v", obj) 241 } 242 243 // If the job is periodic or parameterized it is only garbage collectable if 244 // it is stopped. 245 periodic := j.Periodic != nil && j.Periodic.Enabled 246 parameterized := j.IsParameterized() 247 if periodic || parameterized { 248 return j.Stop, nil 249 } 250 251 // If the job isn't dead it isn't eligible 252 if j.Status != structs.JobStatusDead { 253 return false, nil 254 } 255 256 // Any job that is stopped is eligible for garbage collection 257 if j.Stop { 258 return true, nil 259 } 260 261 // Otherwise, only batch jobs are eligible because they complete on their 262 // own without a user stopping them. 263 if j.Type != structs.JobTypeBatch { 264 return false, nil 265 } 266 267 return true, nil 268 } 269 270 // jobIsPeriodic satisfies the ConditionalIndexFunc interface and creates an index 271 // on whether a job is periodic. 272 func jobIsPeriodic(obj interface{}) (bool, error) { 273 j, ok := obj.(*structs.Job) 274 if !ok { 275 return false, fmt.Errorf("Unexpected type: %v", obj) 276 } 277 278 if j.Periodic != nil && j.Periodic.Enabled == true { 279 return true, nil 280 } 281 282 return false, nil 283 } 284 285 // deploymentSchema returns the MemDB schema tracking a job's deployments 286 func deploymentSchema() *memdb.TableSchema { 287 return &memdb.TableSchema{ 288 Name: "deployment", 289 Indexes: map[string]*memdb.IndexSchema{ 290 "id": { 291 Name: "id", 292 AllowMissing: false, 293 Unique: true, 294 Indexer: &memdb.UUIDFieldIndex{ 295 Field: "ID", 296 }, 297 }, 298 299 "namespace": { 300 Name: "namespace", 301 AllowMissing: false, 302 Unique: false, 303 Indexer: &memdb.StringFieldIndex{ 304 Field: "Namespace", 305 }, 306 }, 307 308 // Job index is used to lookup deployments by job 309 "job": { 310 Name: "job", 311 AllowMissing: false, 312 Unique: false, 313 314 // Use a compound index so the tuple of (Namespace, JobID) is 315 // uniquely identifying 316 Indexer: &memdb.CompoundIndex{ 317 Indexes: []memdb.Indexer{ 318 &memdb.StringFieldIndex{ 319 Field: "Namespace", 320 }, 321 322 &memdb.StringFieldIndex{ 323 Field: "JobID", 324 }, 325 }, 326 }, 327 }, 328 }, 329 } 330 } 331 332 // periodicLaunchTableSchema returns the MemDB schema tracking the most recent 333 // launch time for a periodic job. 334 func periodicLaunchTableSchema() *memdb.TableSchema { 335 return &memdb.TableSchema{ 336 Name: "periodic_launch", 337 Indexes: map[string]*memdb.IndexSchema{ 338 // Primary index is used for job management 339 // and simple direct lookup. ID is required to be 340 // unique. 341 "id": { 342 Name: "id", 343 AllowMissing: false, 344 Unique: true, 345 346 // Use a compound index so the tuple of (Namespace, JobID) is 347 // uniquely identifying 348 Indexer: &memdb.CompoundIndex{ 349 Indexes: []memdb.Indexer{ 350 &memdb.StringFieldIndex{ 351 Field: "Namespace", 352 }, 353 354 &memdb.StringFieldIndex{ 355 Field: "ID", 356 }, 357 }, 358 }, 359 }, 360 }, 361 } 362 } 363 364 // evalTableSchema returns the MemDB schema for the eval table. 365 // This table is used to store all the evaluations that are pending 366 // or recently completed. 367 func evalTableSchema() *memdb.TableSchema { 368 return &memdb.TableSchema{ 369 Name: "evals", 370 Indexes: map[string]*memdb.IndexSchema{ 371 // Primary index is used for direct lookup. 372 "id": { 373 Name: "id", 374 AllowMissing: false, 375 Unique: true, 376 Indexer: &memdb.UUIDFieldIndex{ 377 Field: "ID", 378 }, 379 }, 380 381 "namespace": { 382 Name: "namespace", 383 AllowMissing: false, 384 Unique: false, 385 Indexer: &memdb.StringFieldIndex{ 386 Field: "Namespace", 387 }, 388 }, 389 390 // Job index is used to lookup allocations by job 391 "job": { 392 Name: "job", 393 AllowMissing: false, 394 Unique: false, 395 Indexer: &memdb.CompoundIndex{ 396 Indexes: []memdb.Indexer{ 397 &memdb.StringFieldIndex{ 398 Field: "Namespace", 399 }, 400 401 &memdb.StringFieldIndex{ 402 Field: "JobID", 403 Lowercase: true, 404 }, 405 406 &memdb.StringFieldIndex{ 407 Field: "Status", 408 Lowercase: true, 409 }, 410 }, 411 }, 412 }, 413 }, 414 } 415 } 416 417 // allocTableSchema returns the MemDB schema for the allocation table. 418 // This table is used to store all the task allocations between task groups 419 // and nodes. 420 func allocTableSchema() *memdb.TableSchema { 421 return &memdb.TableSchema{ 422 Name: "allocs", 423 Indexes: map[string]*memdb.IndexSchema{ 424 // Primary index is a UUID 425 "id": { 426 Name: "id", 427 AllowMissing: false, 428 Unique: true, 429 Indexer: &memdb.UUIDFieldIndex{ 430 Field: "ID", 431 }, 432 }, 433 434 "namespace": { 435 Name: "namespace", 436 AllowMissing: false, 437 Unique: false, 438 Indexer: &memdb.StringFieldIndex{ 439 Field: "Namespace", 440 }, 441 }, 442 443 // Node index is used to lookup allocations by node 444 "node": { 445 Name: "node", 446 AllowMissing: true, // Missing is allow for failed allocations 447 Unique: false, 448 Indexer: &memdb.CompoundIndex{ 449 Indexes: []memdb.Indexer{ 450 &memdb.StringFieldIndex{ 451 Field: "NodeID", 452 Lowercase: true, 453 }, 454 455 // Conditional indexer on if allocation is terminal 456 &memdb.ConditionalIndex{ 457 Conditional: func(obj interface{}) (bool, error) { 458 // Cast to allocation 459 alloc, ok := obj.(*structs.Allocation) 460 if !ok { 461 return false, fmt.Errorf("wrong type, got %t should be Allocation", obj) 462 } 463 464 // Check if the allocation is terminal 465 return alloc.TerminalStatus(), nil 466 }, 467 }, 468 }, 469 }, 470 }, 471 472 // Job index is used to lookup allocations by job 473 "job": { 474 Name: "job", 475 AllowMissing: false, 476 Unique: false, 477 478 Indexer: &memdb.CompoundIndex{ 479 Indexes: []memdb.Indexer{ 480 &memdb.StringFieldIndex{ 481 Field: "Namespace", 482 }, 483 484 &memdb.StringFieldIndex{ 485 Field: "JobID", 486 }, 487 }, 488 }, 489 }, 490 491 // Eval index is used to lookup allocations by eval 492 "eval": { 493 Name: "eval", 494 AllowMissing: false, 495 Unique: false, 496 Indexer: &memdb.UUIDFieldIndex{ 497 Field: "EvalID", 498 }, 499 }, 500 501 // Deployment index is used to lookup allocations by deployment 502 "deployment": { 503 Name: "deployment", 504 AllowMissing: true, 505 Unique: false, 506 Indexer: &memdb.UUIDFieldIndex{ 507 Field: "DeploymentID", 508 }, 509 }, 510 }, 511 } 512 } 513 514 // vaultAccessorTableSchema returns the MemDB schema for the Vault Accessor 515 // Table. This table tracks Vault accessors for tokens created on behalf of 516 // allocations required Vault tokens. 517 func vaultAccessorTableSchema() *memdb.TableSchema { 518 return &memdb.TableSchema{ 519 Name: "vault_accessors", 520 Indexes: map[string]*memdb.IndexSchema{ 521 // The primary index is the accessor id 522 "id": { 523 Name: "id", 524 AllowMissing: false, 525 Unique: true, 526 Indexer: &memdb.StringFieldIndex{ 527 Field: "Accessor", 528 }, 529 }, 530 531 "alloc_id": { 532 Name: "alloc_id", 533 AllowMissing: false, 534 Unique: false, 535 Indexer: &memdb.StringFieldIndex{ 536 Field: "AllocID", 537 }, 538 }, 539 540 "node_id": { 541 Name: "node_id", 542 AllowMissing: false, 543 Unique: false, 544 Indexer: &memdb.StringFieldIndex{ 545 Field: "NodeID", 546 }, 547 }, 548 }, 549 } 550 } 551 552 // aclPolicyTableSchema returns the MemDB schema for the policy table. 553 // This table is used to store the policies which are referenced by tokens 554 func aclPolicyTableSchema() *memdb.TableSchema { 555 return &memdb.TableSchema{ 556 Name: "acl_policy", 557 Indexes: map[string]*memdb.IndexSchema{ 558 "id": { 559 Name: "id", 560 AllowMissing: false, 561 Unique: true, 562 Indexer: &memdb.StringFieldIndex{ 563 Field: "Name", 564 }, 565 }, 566 }, 567 } 568 } 569 570 // aclTokenTableSchema returns the MemDB schema for the tokens table. 571 // This table is used to store the bearer tokens which are used to authenticate 572 func aclTokenTableSchema() *memdb.TableSchema { 573 return &memdb.TableSchema{ 574 Name: "acl_token", 575 Indexes: map[string]*memdb.IndexSchema{ 576 "id": { 577 Name: "id", 578 AllowMissing: false, 579 Unique: true, 580 Indexer: &memdb.UUIDFieldIndex{ 581 Field: "AccessorID", 582 }, 583 }, 584 "secret": { 585 Name: "secret", 586 AllowMissing: false, 587 Unique: true, 588 Indexer: &memdb.UUIDFieldIndex{ 589 Field: "SecretID", 590 }, 591 }, 592 "global": { 593 Name: "global", 594 AllowMissing: false, 595 Unique: false, 596 Indexer: &memdb.FieldSetIndex{ 597 Field: "Global", 598 }, 599 }, 600 }, 601 } 602 } 603 604 // schedulerConfigTableSchema returns the MemDB schema for the scheduler config table. 605 // This table is used to store configuration options for the scheduler 606 func schedulerConfigTableSchema() *memdb.TableSchema { 607 return &memdb.TableSchema{ 608 Name: "scheduler_config", 609 Indexes: map[string]*memdb.IndexSchema{ 610 "id": { 611 Name: "id", 612 AllowMissing: true, 613 Unique: true, 614 // This indexer ensures that this table is a singleton 615 Indexer: &memdb.ConditionalIndex{ 616 Conditional: func(obj interface{}) (bool, error) { return true, nil }, 617 }, 618 }, 619 }, 620 } 621 }