github.com/anuvu/nomad@v0.8.7-atom1/nomad/state/schema.go (about)

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