github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/nomad/state/schema.go (about) 1 package state 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/go-memdb" 7 "github.com/hashicorp/nomad/nomad/structs" 8 ) 9 10 // stateStoreSchema is used to return the schema for the state store 11 func stateStoreSchema() *memdb.DBSchema { 12 // Create the root DB schema 13 db := &memdb.DBSchema{ 14 Tables: make(map[string]*memdb.TableSchema), 15 } 16 17 // Collect all the schemas that are needed 18 schemas := []func() *memdb.TableSchema{ 19 indexTableSchema, 20 nodeTableSchema, 21 jobTableSchema, 22 jobSummarySchema, 23 jobVersionSchema, 24 deploymentSchema, 25 periodicLaunchTableSchema, 26 evalTableSchema, 27 allocTableSchema, 28 vaultAccessorTableSchema, 29 } 30 31 // Add each of the tables 32 for _, schemaFn := range schemas { 33 schema := schemaFn() 34 if _, ok := db.Tables[schema.Name]; ok { 35 panic(fmt.Sprintf("duplicate table name: %s", schema.Name)) 36 } 37 db.Tables[schema.Name] = schema 38 } 39 return db 40 } 41 42 // indexTableSchema is used for 43 func indexTableSchema() *memdb.TableSchema { 44 return &memdb.TableSchema{ 45 Name: "index", 46 Indexes: map[string]*memdb.IndexSchema{ 47 "id": &memdb.IndexSchema{ 48 Name: "id", 49 AllowMissing: false, 50 Unique: true, 51 Indexer: &memdb.StringFieldIndex{ 52 Field: "Key", 53 Lowercase: true, 54 }, 55 }, 56 }, 57 } 58 } 59 60 // nodeTableSchema returns the MemDB schema for the nodes table. 61 // This table is used to store all the client nodes that are registered. 62 func nodeTableSchema() *memdb.TableSchema { 63 return &memdb.TableSchema{ 64 Name: "nodes", 65 Indexes: map[string]*memdb.IndexSchema{ 66 // Primary index is used for node management 67 // and simple direct lookup. ID is required to be 68 // unique. 69 "id": &memdb.IndexSchema{ 70 Name: "id", 71 AllowMissing: false, 72 Unique: true, 73 Indexer: &memdb.UUIDFieldIndex{ 74 Field: "ID", 75 }, 76 }, 77 }, 78 } 79 } 80 81 // jobTableSchema returns the MemDB schema for the jobs table. 82 // This table is used to store all the jobs that have been submitted. 83 func jobTableSchema() *memdb.TableSchema { 84 return &memdb.TableSchema{ 85 Name: "jobs", 86 Indexes: map[string]*memdb.IndexSchema{ 87 // Primary index is used for job management 88 // and simple direct lookup. ID is required to be 89 // unique. 90 "id": &memdb.IndexSchema{ 91 Name: "id", 92 AllowMissing: false, 93 Unique: true, 94 Indexer: &memdb.StringFieldIndex{ 95 Field: "ID", 96 Lowercase: true, 97 }, 98 }, 99 "type": &memdb.IndexSchema{ 100 Name: "type", 101 AllowMissing: false, 102 Unique: false, 103 Indexer: &memdb.StringFieldIndex{ 104 Field: "Type", 105 Lowercase: false, 106 }, 107 }, 108 "gc": &memdb.IndexSchema{ 109 Name: "gc", 110 AllowMissing: false, 111 Unique: false, 112 Indexer: &memdb.ConditionalIndex{ 113 Conditional: jobIsGCable, 114 }, 115 }, 116 "periodic": &memdb.IndexSchema{ 117 Name: "periodic", 118 AllowMissing: false, 119 Unique: false, 120 Indexer: &memdb.ConditionalIndex{ 121 Conditional: jobIsPeriodic, 122 }, 123 }, 124 }, 125 } 126 } 127 128 // jobSummarySchema returns the memdb schema for the job summary table 129 func jobSummarySchema() *memdb.TableSchema { 130 return &memdb.TableSchema{ 131 Name: "job_summary", 132 Indexes: map[string]*memdb.IndexSchema{ 133 "id": &memdb.IndexSchema{ 134 Name: "id", 135 AllowMissing: false, 136 Unique: true, 137 Indexer: &memdb.StringFieldIndex{ 138 Field: "JobID", 139 Lowercase: true, 140 }, 141 }, 142 }, 143 } 144 } 145 146 // jobVersionSchema returns the memdb schema for the job version table which 147 // keeps a historical view of job versions. 148 func jobVersionSchema() *memdb.TableSchema { 149 return &memdb.TableSchema{ 150 Name: "job_version", 151 Indexes: map[string]*memdb.IndexSchema{ 152 "id": &memdb.IndexSchema{ 153 Name: "id", 154 AllowMissing: false, 155 Unique: true, 156 157 // Use a compound index so the tuple of (JobID, Version) is 158 // uniquely identifying 159 Indexer: &memdb.CompoundIndex{ 160 Indexes: []memdb.Indexer{ 161 &memdb.StringFieldIndex{ 162 Field: "ID", 163 Lowercase: true, 164 }, 165 166 // Will need to create a new indexer 167 &memdb.UintFieldIndex{ 168 Field: "Version", 169 }, 170 }, 171 }, 172 }, 173 }, 174 } 175 } 176 177 // jobIsGCable satisfies the ConditionalIndexFunc interface and creates an index 178 // on whether a job is eligible for garbage collection. 179 func jobIsGCable(obj interface{}) (bool, error) { 180 j, ok := obj.(*structs.Job) 181 if !ok { 182 return false, fmt.Errorf("Unexpected type: %v", obj) 183 } 184 185 // If the job is periodic or parameterized it is only garbage collectable if 186 // it is stopped. 187 periodic := j.Periodic != nil && j.Periodic.Enabled 188 parameterized := j.IsParameterized() 189 if periodic || parameterized { 190 return j.Stop, nil 191 } 192 193 // If the job isn't dead it isn't eligible 194 if j.Status != structs.JobStatusDead { 195 return false, nil 196 } 197 198 // Any job that is stopped is eligible for garbage collection 199 if j.Stop { 200 return true, nil 201 } 202 203 // Otherwise, only batch jobs are eligible because they complete on their 204 // own without a user stopping them. 205 if j.Type != structs.JobTypeBatch { 206 return false, nil 207 } 208 209 return true, nil 210 } 211 212 // jobIsPeriodic satisfies the ConditionalIndexFunc interface and creates an index 213 // on whether a job is periodic. 214 func jobIsPeriodic(obj interface{}) (bool, error) { 215 j, ok := obj.(*structs.Job) 216 if !ok { 217 return false, fmt.Errorf("Unexpected type: %v", obj) 218 } 219 220 if j.Periodic != nil && j.Periodic.Enabled == true { 221 return true, nil 222 } 223 224 return false, nil 225 } 226 227 // deploymentSchema returns the MemDB schema tracking a job's deployments 228 func deploymentSchema() *memdb.TableSchema { 229 return &memdb.TableSchema{ 230 Name: "deployment", 231 Indexes: map[string]*memdb.IndexSchema{ 232 "id": &memdb.IndexSchema{ 233 Name: "id", 234 AllowMissing: false, 235 Unique: true, 236 Indexer: &memdb.UUIDFieldIndex{ 237 Field: "ID", 238 }, 239 }, 240 241 // Job index is used to lookup deployments by job 242 "job": &memdb.IndexSchema{ 243 Name: "job", 244 AllowMissing: false, 245 Unique: false, 246 Indexer: &memdb.StringFieldIndex{ 247 Field: "JobID", 248 Lowercase: true, 249 }, 250 }, 251 }, 252 } 253 } 254 255 // periodicLaunchTableSchema returns the MemDB schema tracking the most recent 256 // launch time for a perioidic job. 257 func periodicLaunchTableSchema() *memdb.TableSchema { 258 return &memdb.TableSchema{ 259 Name: "periodic_launch", 260 Indexes: map[string]*memdb.IndexSchema{ 261 // Primary index is used for job management 262 // and simple direct lookup. ID is required to be 263 // unique. 264 "id": &memdb.IndexSchema{ 265 Name: "id", 266 AllowMissing: false, 267 Unique: true, 268 Indexer: &memdb.StringFieldIndex{ 269 Field: "ID", 270 Lowercase: true, 271 }, 272 }, 273 }, 274 } 275 } 276 277 // evalTableSchema returns the MemDB schema for the eval table. 278 // This table is used to store all the evaluations that are pending 279 // or recently completed. 280 func evalTableSchema() *memdb.TableSchema { 281 return &memdb.TableSchema{ 282 Name: "evals", 283 Indexes: map[string]*memdb.IndexSchema{ 284 // Primary index is used for direct lookup. 285 "id": &memdb.IndexSchema{ 286 Name: "id", 287 AllowMissing: false, 288 Unique: true, 289 Indexer: &memdb.UUIDFieldIndex{ 290 Field: "ID", 291 }, 292 }, 293 294 // Job index is used to lookup allocations by job 295 "job": &memdb.IndexSchema{ 296 Name: "job", 297 AllowMissing: false, 298 Unique: false, 299 Indexer: &memdb.CompoundIndex{ 300 Indexes: []memdb.Indexer{ 301 &memdb.StringFieldIndex{ 302 Field: "JobID", 303 Lowercase: true, 304 }, 305 &memdb.StringFieldIndex{ 306 Field: "Status", 307 Lowercase: true, 308 }, 309 }, 310 }, 311 }, 312 }, 313 } 314 } 315 316 // allocTableSchema returns the MemDB schema for the allocation table. 317 // This table is used to store all the task allocations between task groups 318 // and nodes. 319 func allocTableSchema() *memdb.TableSchema { 320 return &memdb.TableSchema{ 321 Name: "allocs", 322 Indexes: map[string]*memdb.IndexSchema{ 323 // Primary index is a UUID 324 "id": &memdb.IndexSchema{ 325 Name: "id", 326 AllowMissing: false, 327 Unique: true, 328 Indexer: &memdb.UUIDFieldIndex{ 329 Field: "ID", 330 }, 331 }, 332 333 // Node index is used to lookup allocations by node 334 "node": &memdb.IndexSchema{ 335 Name: "node", 336 AllowMissing: true, // Missing is allow for failed allocations 337 Unique: false, 338 Indexer: &memdb.CompoundIndex{ 339 Indexes: []memdb.Indexer{ 340 &memdb.StringFieldIndex{ 341 Field: "NodeID", 342 Lowercase: true, 343 }, 344 345 // Conditional indexer on if allocation is terminal 346 &memdb.ConditionalIndex{ 347 Conditional: func(obj interface{}) (bool, error) { 348 // Cast to allocation 349 alloc, ok := obj.(*structs.Allocation) 350 if !ok { 351 return false, fmt.Errorf("wrong type, got %t should be Allocation", obj) 352 } 353 354 // Check if the allocation is terminal 355 return alloc.TerminalStatus(), nil 356 }, 357 }, 358 }, 359 }, 360 }, 361 362 // Job index is used to lookup allocations by job 363 "job": &memdb.IndexSchema{ 364 Name: "job", 365 AllowMissing: false, 366 Unique: false, 367 Indexer: &memdb.StringFieldIndex{ 368 Field: "JobID", 369 Lowercase: true, 370 }, 371 }, 372 373 // Eval index is used to lookup allocations by eval 374 "eval": &memdb.IndexSchema{ 375 Name: "eval", 376 AllowMissing: false, 377 Unique: false, 378 Indexer: &memdb.UUIDFieldIndex{ 379 Field: "EvalID", 380 }, 381 }, 382 383 // Deployment index is used to lookup allocations by deployment 384 "deployment": &memdb.IndexSchema{ 385 Name: "deployment", 386 AllowMissing: true, 387 Unique: false, 388 Indexer: &memdb.UUIDFieldIndex{ 389 Field: "DeploymentID", 390 }, 391 }, 392 }, 393 } 394 } 395 396 // vaultAccessorTableSchema returns the MemDB schema for the Vault Accessor 397 // Table. This table tracks Vault accessors for tokens created on behalf of 398 // allocations required Vault tokens. 399 func vaultAccessorTableSchema() *memdb.TableSchema { 400 return &memdb.TableSchema{ 401 Name: "vault_accessors", 402 Indexes: map[string]*memdb.IndexSchema{ 403 // The primary index is the accessor id 404 "id": &memdb.IndexSchema{ 405 Name: "id", 406 AllowMissing: false, 407 Unique: true, 408 Indexer: &memdb.StringFieldIndex{ 409 Field: "Accessor", 410 }, 411 }, 412 413 "alloc_id": &memdb.IndexSchema{ 414 Name: "alloc_id", 415 AllowMissing: false, 416 Unique: false, 417 Indexer: &memdb.StringFieldIndex{ 418 Field: "AllocID", 419 }, 420 }, 421 422 "node_id": &memdb.IndexSchema{ 423 Name: "node_id", 424 AllowMissing: false, 425 Unique: false, 426 Indexer: &memdb.StringFieldIndex{ 427 Field: "NodeID", 428 }, 429 }, 430 }, 431 } 432 }