github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/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/state/indexer"
     9  	"github.com/hashicorp/nomad/nomad/structs"
    10  )
    11  
    12  const (
    13  	tableIndex = "index"
    14  
    15  	TableNamespaces           = "namespaces"
    16  	TableServiceRegistrations = "service_registrations"
    17  	TableVariables            = "variables"
    18  	TableVariablesQuotas      = "variables_quota"
    19  	TableRootKeyMeta          = "root_key_meta"
    20  	TableACLRoles             = "acl_roles"
    21  	TableACLAuthMethods       = "acl_auth_methods"
    22  	TableACLBindingRules      = "acl_binding_rules"
    23  	TableAllocs               = "allocs"
    24  )
    25  
    26  const (
    27  	indexID            = "id"
    28  	indexJob           = "job"
    29  	indexNodeID        = "node_id"
    30  	indexAllocID       = "alloc_id"
    31  	indexServiceName   = "service_name"
    32  	indexExpiresGlobal = "expires-global"
    33  	indexExpiresLocal  = "expires-local"
    34  	indexKeyID         = "key_id"
    35  	indexPath          = "path"
    36  	indexName          = "name"
    37  	indexSigningKey    = "signing_key"
    38  	indexAuthMethod    = "auth_method"
    39  )
    40  
    41  var (
    42  	schemaFactories SchemaFactories
    43  	factoriesLock   sync.Mutex
    44  )
    45  
    46  // SchemaFactory is the factory method for returning a TableSchema
    47  type SchemaFactory func() *memdb.TableSchema
    48  type SchemaFactories []SchemaFactory
    49  
    50  // RegisterSchemaFactories is used to register a table schema.
    51  func RegisterSchemaFactories(factories ...SchemaFactory) {
    52  	factoriesLock.Lock()
    53  	defer factoriesLock.Unlock()
    54  	schemaFactories = append(schemaFactories, factories...)
    55  }
    56  
    57  func GetFactories() SchemaFactories {
    58  	return schemaFactories
    59  }
    60  
    61  func init() {
    62  	// Register all schemas
    63  	RegisterSchemaFactories([]SchemaFactory{
    64  		indexTableSchema,
    65  		nodeTableSchema,
    66  		jobTableSchema,
    67  		jobSummarySchema,
    68  		jobVersionSchema,
    69  		deploymentSchema,
    70  		periodicLaunchTableSchema,
    71  		evalTableSchema,
    72  		allocTableSchema,
    73  		vaultAccessorTableSchema,
    74  		siTokenAccessorTableSchema,
    75  		aclPolicyTableSchema,
    76  		aclTokenTableSchema,
    77  		oneTimeTokenTableSchema,
    78  		autopilotConfigTableSchema,
    79  		schedulerConfigTableSchema,
    80  		clusterMetaTableSchema,
    81  		csiVolumeTableSchema,
    82  		csiPluginTableSchema,
    83  		scalingPolicyTableSchema,
    84  		scalingEventTableSchema,
    85  		namespaceTableSchema,
    86  		serviceRegistrationsTableSchema,
    87  		variablesTableSchema,
    88  		variablesQuotasTableSchema,
    89  		variablesRootKeyMetaSchema,
    90  		aclRolesTableSchema,
    91  		aclAuthMethodsTableSchema,
    92  		bindingRulesTableSchema,
    93  	}...)
    94  }
    95  
    96  // stateStoreSchema is used to return the schema for the state store
    97  func stateStoreSchema() *memdb.DBSchema {
    98  	// Create the root DB schema
    99  	db := &memdb.DBSchema{
   100  		Tables: make(map[string]*memdb.TableSchema),
   101  	}
   102  
   103  	// Add each of the tables
   104  	for _, schemaFn := range GetFactories() {
   105  		schema := schemaFn()
   106  		if _, ok := db.Tables[schema.Name]; ok {
   107  			panic(fmt.Sprintf("duplicate table name: %s", schema.Name))
   108  		}
   109  		db.Tables[schema.Name] = schema
   110  	}
   111  	return db
   112  }
   113  
   114  // indexTableSchema is used for tracking the most recent index used for each table.
   115  func indexTableSchema() *memdb.TableSchema {
   116  	return &memdb.TableSchema{
   117  		Name: "index",
   118  		Indexes: map[string]*memdb.IndexSchema{
   119  			"id": {
   120  				Name:         "id",
   121  				AllowMissing: false,
   122  				Unique:       true,
   123  				Indexer: &memdb.StringFieldIndex{
   124  					Field:     "Key",
   125  					Lowercase: true,
   126  				},
   127  			},
   128  		},
   129  	}
   130  }
   131  
   132  // nodeTableSchema returns the MemDB schema for the nodes table.
   133  // This table is used to store all the client nodes that are registered.
   134  func nodeTableSchema() *memdb.TableSchema {
   135  	return &memdb.TableSchema{
   136  		Name: "nodes",
   137  		Indexes: map[string]*memdb.IndexSchema{
   138  			// Primary index is used for node management
   139  			// and simple direct lookup. ID is required to be
   140  			// unique.
   141  			"id": {
   142  				Name:         "id",
   143  				AllowMissing: false,
   144  				Unique:       true,
   145  				Indexer: &memdb.UUIDFieldIndex{
   146  					Field: "ID",
   147  				},
   148  			},
   149  			"secret_id": {
   150  				Name:         "secret_id",
   151  				AllowMissing: false,
   152  				Unique:       true,
   153  				Indexer: &memdb.UUIDFieldIndex{
   154  					Field: "SecretID",
   155  				},
   156  			},
   157  		},
   158  	}
   159  }
   160  
   161  // jobTableSchema returns the MemDB schema for the jobs table.
   162  // This table is used to store all the jobs that have been submitted.
   163  func jobTableSchema() *memdb.TableSchema {
   164  	return &memdb.TableSchema{
   165  		Name: "jobs",
   166  		Indexes: map[string]*memdb.IndexSchema{
   167  			// Primary index is used for job management
   168  			// and simple direct lookup. ID is required to be
   169  			// unique within a namespace.
   170  			"id": {
   171  				Name:         "id",
   172  				AllowMissing: false,
   173  				Unique:       true,
   174  
   175  				// Use a compound index so the tuple of (Namespace, ID) is
   176  				// uniquely identifying
   177  				Indexer: &memdb.CompoundIndex{
   178  					Indexes: []memdb.Indexer{
   179  						&memdb.StringFieldIndex{
   180  							Field: "Namespace",
   181  						},
   182  
   183  						&memdb.StringFieldIndex{
   184  							Field: "ID",
   185  						},
   186  					},
   187  				},
   188  			},
   189  			"type": {
   190  				Name:         "type",
   191  				AllowMissing: false,
   192  				Unique:       false,
   193  				Indexer: &memdb.StringFieldIndex{
   194  					Field:     "Type",
   195  					Lowercase: false,
   196  				},
   197  			},
   198  			"gc": {
   199  				Name:         "gc",
   200  				AllowMissing: false,
   201  				Unique:       false,
   202  				Indexer: &memdb.ConditionalIndex{
   203  					Conditional: jobIsGCable,
   204  				},
   205  			},
   206  			"periodic": {
   207  				Name:         "periodic",
   208  				AllowMissing: false,
   209  				Unique:       false,
   210  				Indexer: &memdb.ConditionalIndex{
   211  					Conditional: jobIsPeriodic,
   212  				},
   213  			},
   214  		},
   215  	}
   216  }
   217  
   218  // jobSummarySchema returns the memdb schema for the job summary table
   219  func jobSummarySchema() *memdb.TableSchema {
   220  	return &memdb.TableSchema{
   221  		Name: "job_summary",
   222  		Indexes: map[string]*memdb.IndexSchema{
   223  			"id": {
   224  				Name:         "id",
   225  				AllowMissing: false,
   226  				Unique:       true,
   227  
   228  				// Use a compound index so the tuple of (Namespace, JobID) is
   229  				// uniquely identifying
   230  				Indexer: &memdb.CompoundIndex{
   231  					Indexes: []memdb.Indexer{
   232  						&memdb.StringFieldIndex{
   233  							Field: "Namespace",
   234  						},
   235  
   236  						&memdb.StringFieldIndex{
   237  							Field: "JobID",
   238  						},
   239  					},
   240  				},
   241  			},
   242  		},
   243  	}
   244  }
   245  
   246  // jobVersionSchema returns the memdb schema for the job version table which
   247  // keeps a historical view of job versions.
   248  func jobVersionSchema() *memdb.TableSchema {
   249  	return &memdb.TableSchema{
   250  		Name: "job_version",
   251  		Indexes: map[string]*memdb.IndexSchema{
   252  			"id": {
   253  				Name:         "id",
   254  				AllowMissing: false,
   255  				Unique:       true,
   256  
   257  				// Use a compound index so the tuple of (Namespace, ID, Version) is
   258  				// uniquely identifying
   259  				Indexer: &memdb.CompoundIndex{
   260  					Indexes: []memdb.Indexer{
   261  						&memdb.StringFieldIndex{
   262  							Field: "Namespace",
   263  						},
   264  
   265  						&memdb.StringFieldIndex{
   266  							Field:     "ID",
   267  							Lowercase: true,
   268  						},
   269  
   270  						&memdb.UintFieldIndex{
   271  							Field: "Version",
   272  						},
   273  					},
   274  				},
   275  			},
   276  		},
   277  	}
   278  }
   279  
   280  // jobIsGCable satisfies the ConditionalIndexFunc interface and creates an index
   281  // on whether a job is eligible for garbage collection.
   282  func jobIsGCable(obj interface{}) (bool, error) {
   283  	j, ok := obj.(*structs.Job)
   284  	if !ok {
   285  		return false, fmt.Errorf("Unexpected type: %v", obj)
   286  	}
   287  
   288  	// If the job is periodic or parameterized it is only garbage collectable if
   289  	// it is stopped.
   290  	periodic := j.Periodic != nil && j.Periodic.Enabled
   291  	parameterized := j.IsParameterized()
   292  	if periodic || parameterized {
   293  		return j.Stop, nil
   294  	}
   295  
   296  	// If the job isn't dead it isn't eligible
   297  	if j.Status != structs.JobStatusDead {
   298  		return false, nil
   299  	}
   300  
   301  	// Any job that is stopped is eligible for garbage collection
   302  	if j.Stop {
   303  		return true, nil
   304  	}
   305  
   306  	switch j.Type {
   307  	// Otherwise, batch and sysbatch jobs are eligible because they complete on
   308  	// their own without a user stopping them.
   309  	case structs.JobTypeBatch, structs.JobTypeSysBatch:
   310  		return true, nil
   311  
   312  	default:
   313  		// other job types may not be GC until stopped
   314  		return false, nil
   315  	}
   316  }
   317  
   318  // jobIsPeriodic satisfies the ConditionalIndexFunc interface and creates an index
   319  // on whether a job is periodic.
   320  func jobIsPeriodic(obj interface{}) (bool, error) {
   321  	j, ok := obj.(*structs.Job)
   322  	if !ok {
   323  		return false, fmt.Errorf("Unexpected type: %v", obj)
   324  	}
   325  
   326  	if j.Periodic != nil && j.Periodic.Enabled {
   327  		return true, nil
   328  	}
   329  
   330  	return false, nil
   331  }
   332  
   333  // deploymentSchema returns the MemDB schema tracking a job's deployments
   334  func deploymentSchema() *memdb.TableSchema {
   335  	return &memdb.TableSchema{
   336  		Name: "deployment",
   337  		Indexes: map[string]*memdb.IndexSchema{
   338  			// id index is used for direct lookup of an deployment by ID.
   339  			"id": {
   340  				Name:         "id",
   341  				AllowMissing: false,
   342  				Unique:       true,
   343  				Indexer: &memdb.UUIDFieldIndex{
   344  					Field: "ID",
   345  				},
   346  			},
   347  
   348  			// create index is used for listing deploy, ordering them by
   349  			// creation chronology. (Use a reverse iterator for newest first).
   350  			//
   351  			// There may be more than one deployment per CreateIndex.
   352  			"create": {
   353  				Name:         "create",
   354  				AllowMissing: false,
   355  				Unique:       true,
   356  				Indexer: &memdb.CompoundIndex{
   357  					Indexes: []memdb.Indexer{
   358  						&memdb.UintFieldIndex{
   359  							Field: "CreateIndex",
   360  						},
   361  						&memdb.StringFieldIndex{
   362  							Field: "ID",
   363  						},
   364  					},
   365  				},
   366  			},
   367  
   368  			// namespace is used to lookup evaluations by namespace.
   369  			"namespace": {
   370  				Name:         "namespace",
   371  				AllowMissing: false,
   372  				Unique:       false,
   373  				Indexer: &memdb.StringFieldIndex{
   374  					Field: "Namespace",
   375  				},
   376  			},
   377  
   378  			// namespace_create index is used to lookup deployments by namespace
   379  			// in their original chronological order based on CreateIndex.
   380  			//
   381  			// Use a prefix iterator (namespace_create_prefix) to iterate deployments
   382  			// of a Namespace in order of CreateIndex.
   383  			//
   384  			// There may be more than one deployment per CreateIndex.
   385  			"namespace_create": {
   386  				Name:         "namespace_create",
   387  				AllowMissing: false,
   388  				Unique:       true,
   389  				Indexer: &memdb.CompoundIndex{
   390  					AllowMissing: false,
   391  					Indexes: []memdb.Indexer{
   392  						&memdb.StringFieldIndex{
   393  							Field: "Namespace",
   394  						},
   395  						&memdb.UintFieldIndex{
   396  							Field: "CreateIndex",
   397  						},
   398  						&memdb.StringFieldIndex{
   399  							Field: "ID",
   400  						},
   401  					},
   402  				},
   403  			},
   404  
   405  			// job index is used to lookup deployments by job
   406  			"job": {
   407  				Name:         "job",
   408  				AllowMissing: false,
   409  				Unique:       false,
   410  
   411  				// Use a compound index so the tuple of (Namespace, JobID) is
   412  				// uniquely identifying
   413  				Indexer: &memdb.CompoundIndex{
   414  					Indexes: []memdb.Indexer{
   415  						&memdb.StringFieldIndex{
   416  							Field: "Namespace",
   417  						},
   418  
   419  						&memdb.StringFieldIndex{
   420  							Field: "JobID",
   421  						},
   422  					},
   423  				},
   424  			},
   425  		},
   426  	}
   427  }
   428  
   429  // periodicLaunchTableSchema returns the MemDB schema tracking the most recent
   430  // launch time for a periodic job.
   431  func periodicLaunchTableSchema() *memdb.TableSchema {
   432  	return &memdb.TableSchema{
   433  		Name: "periodic_launch",
   434  		Indexes: map[string]*memdb.IndexSchema{
   435  			// Primary index is used for job management
   436  			// and simple direct lookup. ID is required to be
   437  			// unique.
   438  			"id": {
   439  				Name:         "id",
   440  				AllowMissing: false,
   441  				Unique:       true,
   442  
   443  				// Use a compound index so the tuple of (Namespace, JobID) is
   444  				// uniquely identifying
   445  				Indexer: &memdb.CompoundIndex{
   446  					Indexes: []memdb.Indexer{
   447  						&memdb.StringFieldIndex{
   448  							Field: "Namespace",
   449  						},
   450  
   451  						&memdb.StringFieldIndex{
   452  							Field: "ID",
   453  						},
   454  					},
   455  				},
   456  			},
   457  		},
   458  	}
   459  }
   460  
   461  // evalTableSchema returns the MemDB schema for the eval table.
   462  // This table is used to store all the evaluations that are pending
   463  // or recently completed.
   464  func evalTableSchema() *memdb.TableSchema {
   465  	return &memdb.TableSchema{
   466  		Name: "evals",
   467  		Indexes: map[string]*memdb.IndexSchema{
   468  			// id index is used for direct lookup of an evaluation by ID.
   469  			"id": {
   470  				Name:         "id",
   471  				AllowMissing: false,
   472  				Unique:       true,
   473  				Indexer: &memdb.UUIDFieldIndex{
   474  					Field: "ID",
   475  				},
   476  			},
   477  
   478  			// create index is used for listing evaluations, ordering them by
   479  			// creation chronology. (Use a reverse iterator for newest first).
   480  			"create": {
   481  				Name:         "create",
   482  				AllowMissing: false,
   483  				Unique:       true,
   484  				Indexer: &memdb.CompoundIndex{
   485  					Indexes: []memdb.Indexer{
   486  						&memdb.UintFieldIndex{
   487  							Field: "CreateIndex",
   488  						},
   489  						&memdb.StringFieldIndex{
   490  							Field: "ID",
   491  						},
   492  					},
   493  				},
   494  			},
   495  
   496  			// job index is used to lookup evaluations by job ID.
   497  			"job": {
   498  				Name:         "job",
   499  				AllowMissing: false,
   500  				Unique:       false,
   501  				Indexer: &memdb.CompoundIndex{
   502  					Indexes: []memdb.Indexer{
   503  						&memdb.StringFieldIndex{
   504  							Field: "Namespace",
   505  						},
   506  
   507  						&memdb.StringFieldIndex{
   508  							Field:     "JobID",
   509  							Lowercase: true,
   510  						},
   511  
   512  						&memdb.StringFieldIndex{
   513  							Field:     "Status",
   514  							Lowercase: true,
   515  						},
   516  					},
   517  				},
   518  			},
   519  
   520  			// namespace is used to lookup evaluations by namespace.
   521  			"namespace": {
   522  				Name:         "namespace",
   523  				AllowMissing: false,
   524  				Unique:       false,
   525  				Indexer: &memdb.StringFieldIndex{
   526  					Field: "Namespace",
   527  				},
   528  			},
   529  
   530  			// namespace_create index is used to lookup evaluations by namespace
   531  			// in their original chronological order based on CreateIndex.
   532  			//
   533  			// Use a prefix iterator (namespace_prefix) on a Namespace to iterate
   534  			// those evaluations in order of CreateIndex.
   535  			"namespace_create": {
   536  				Name:         "namespace_create",
   537  				AllowMissing: false,
   538  				Unique:       true,
   539  				Indexer: &memdb.CompoundIndex{
   540  					AllowMissing: false,
   541  					Indexes: []memdb.Indexer{
   542  						&memdb.StringFieldIndex{
   543  							Field: "Namespace",
   544  						},
   545  						&memdb.UintFieldIndex{
   546  							Field: "CreateIndex",
   547  						},
   548  						&memdb.StringFieldIndex{
   549  							Field: "ID",
   550  						},
   551  					},
   552  				},
   553  			},
   554  		},
   555  	}
   556  }
   557  
   558  // allocTableSchema returns the MemDB schema for the allocation table.
   559  // This table is used to store all the task allocations between task groups
   560  // and nodes.
   561  func allocTableSchema() *memdb.TableSchema {
   562  	return &memdb.TableSchema{
   563  		Name: "allocs",
   564  		Indexes: map[string]*memdb.IndexSchema{
   565  			// id index is used for direct lookup of allocation by ID.
   566  			"id": {
   567  				Name:         "id",
   568  				AllowMissing: false,
   569  				Unique:       true,
   570  				Indexer: &memdb.UUIDFieldIndex{
   571  					Field: "ID",
   572  				},
   573  			},
   574  
   575  			// create index is used for listing allocations, ordering them by
   576  			// creation chronology. (Use a reverse iterator for newest first).
   577  			"create": {
   578  				Name:         "create",
   579  				AllowMissing: false,
   580  				Unique:       true,
   581  				Indexer: &memdb.CompoundIndex{
   582  					Indexes: []memdb.Indexer{
   583  						&memdb.UintFieldIndex{
   584  							Field: "CreateIndex",
   585  						},
   586  						&memdb.StringFieldIndex{
   587  							Field: "ID",
   588  						},
   589  					},
   590  				},
   591  			},
   592  
   593  			// namespace is used to lookup evaluations by namespace.
   594  			// todo(shoenig): i think we can deprecate this and other like it
   595  			"namespace": {
   596  				Name:         "namespace",
   597  				AllowMissing: false,
   598  				Unique:       false,
   599  				Indexer: &memdb.StringFieldIndex{
   600  					Field: "Namespace",
   601  				},
   602  			},
   603  
   604  			// namespace_create index is used to lookup evaluations by namespace
   605  			// in their original chronological order based on CreateIndex.
   606  			//
   607  			// Use a prefix iterator (namespace_prefix) on a Namespace to iterate
   608  			// those evaluations in order of CreateIndex.
   609  			"namespace_create": {
   610  				Name:         "namespace_create",
   611  				AllowMissing: false,
   612  				Unique:       true,
   613  				Indexer: &memdb.CompoundIndex{
   614  					AllowMissing: false,
   615  					Indexes: []memdb.Indexer{
   616  						&memdb.StringFieldIndex{
   617  							Field: "Namespace",
   618  						},
   619  						&memdb.UintFieldIndex{
   620  							Field: "CreateIndex",
   621  						},
   622  						&memdb.StringFieldIndex{
   623  							Field: "ID",
   624  						},
   625  					},
   626  				},
   627  			},
   628  
   629  			// Node index is used to lookup allocations by node
   630  			"node": {
   631  				Name:         "node",
   632  				AllowMissing: true, // Missing is allow for failed allocations
   633  				Unique:       false,
   634  				Indexer: &memdb.CompoundIndex{
   635  					Indexes: []memdb.Indexer{
   636  						&memdb.StringFieldIndex{
   637  							Field:     "NodeID",
   638  							Lowercase: true,
   639  						},
   640  
   641  						// Conditional indexer on if allocation is terminal
   642  						&memdb.ConditionalIndex{
   643  							Conditional: func(obj interface{}) (bool, error) {
   644  								// Cast to allocation
   645  								alloc, ok := obj.(*structs.Allocation)
   646  								if !ok {
   647  									return false, fmt.Errorf("wrong type, got %t should be Allocation", obj)
   648  								}
   649  
   650  								// Check if the allocation is terminal
   651  								return alloc.TerminalStatus(), nil
   652  							},
   653  						},
   654  					},
   655  				},
   656  			},
   657  
   658  			// Job index is used to lookup allocations by job
   659  			"job": {
   660  				Name:         "job",
   661  				AllowMissing: false,
   662  				Unique:       false,
   663  
   664  				Indexer: &memdb.CompoundIndex{
   665  					Indexes: []memdb.Indexer{
   666  						&memdb.StringFieldIndex{
   667  							Field: "Namespace",
   668  						},
   669  
   670  						&memdb.StringFieldIndex{
   671  							Field: "JobID",
   672  						},
   673  					},
   674  				},
   675  			},
   676  
   677  			// Eval index is used to lookup allocations by eval
   678  			"eval": {
   679  				Name:         "eval",
   680  				AllowMissing: false,
   681  				Unique:       false,
   682  				Indexer: &memdb.UUIDFieldIndex{
   683  					Field: "EvalID",
   684  				},
   685  			},
   686  
   687  			// Deployment index is used to lookup allocations by deployment
   688  			"deployment": {
   689  				Name:         "deployment",
   690  				AllowMissing: true,
   691  				Unique:       false,
   692  				Indexer: &memdb.UUIDFieldIndex{
   693  					Field: "DeploymentID",
   694  				},
   695  			},
   696  
   697  			// signing_key index is used to lookup live allocations by signing
   698  			// key ID
   699  			indexSigningKey: {
   700  				Name:         indexSigningKey,
   701  				AllowMissing: true, // terminal allocations won't be indexed
   702  				Unique:       false,
   703  				Indexer: &memdb.CompoundIndex{
   704  					Indexes: []memdb.Indexer{
   705  						&memdb.StringFieldIndex{
   706  							Field: "SigningKeyID",
   707  						},
   708  						&memdb.ConditionalIndex{
   709  							Conditional: func(obj interface{}) (bool, error) {
   710  								alloc, ok := obj.(*structs.Allocation)
   711  								if !ok {
   712  									return false, fmt.Errorf(
   713  										"wrong type, got %t should be Allocation", obj)
   714  								}
   715  								// note: this isn't alloc.TerminalStatus(),
   716  								// because we only want to consider the key
   717  								// unused if the allocation is terminal on both
   718  								// server and client
   719  								return !(alloc.ClientTerminalStatus() && alloc.ServerTerminalStatus()), nil
   720  							},
   721  						},
   722  					},
   723  				},
   724  			},
   725  		},
   726  	}
   727  }
   728  
   729  // vaultAccessorTableSchema returns the MemDB schema for the Vault Accessor
   730  // Table. This table tracks Vault accessors for tokens created on behalf of
   731  // allocations required Vault tokens.
   732  func vaultAccessorTableSchema() *memdb.TableSchema {
   733  	return &memdb.TableSchema{
   734  		Name: "vault_accessors",
   735  		Indexes: map[string]*memdb.IndexSchema{
   736  			// The primary index is the accessor id
   737  			"id": {
   738  				Name:         "id",
   739  				AllowMissing: false,
   740  				Unique:       true,
   741  				Indexer: &memdb.StringFieldIndex{
   742  					Field: "Accessor",
   743  				},
   744  			},
   745  
   746  			"alloc_id": {
   747  				Name:         "alloc_id",
   748  				AllowMissing: false,
   749  				Unique:       false,
   750  				Indexer: &memdb.StringFieldIndex{
   751  					Field: "AllocID",
   752  				},
   753  			},
   754  
   755  			"node_id": {
   756  				Name:         "node_id",
   757  				AllowMissing: false,
   758  				Unique:       false,
   759  				Indexer: &memdb.StringFieldIndex{
   760  					Field: "NodeID",
   761  				},
   762  			},
   763  		},
   764  	}
   765  }
   766  
   767  // siTokenAccessorTableSchema returns the MemDB schema for the Service Identity
   768  // token accessor table. This table tracks accessors for tokens created on behalf
   769  // of allocations with Consul connect enabled tasks that need SI tokens.
   770  func siTokenAccessorTableSchema() *memdb.TableSchema {
   771  	return &memdb.TableSchema{
   772  		Name: siTokenAccessorTable,
   773  		Indexes: map[string]*memdb.IndexSchema{
   774  			// The primary index is the accessor id
   775  			"id": {
   776  				Name:         "id",
   777  				AllowMissing: false,
   778  				Unique:       true,
   779  				Indexer: &memdb.StringFieldIndex{
   780  					Field: "AccessorID",
   781  				},
   782  			},
   783  
   784  			"alloc_id": {
   785  				Name:         "alloc_id",
   786  				AllowMissing: false,
   787  				Unique:       false,
   788  				Indexer: &memdb.StringFieldIndex{
   789  					Field: "AllocID",
   790  				},
   791  			},
   792  
   793  			"node_id": {
   794  				Name:         "node_id",
   795  				AllowMissing: false,
   796  				Unique:       false,
   797  				Indexer: &memdb.StringFieldIndex{
   798  					Field: "NodeID",
   799  				},
   800  			},
   801  		},
   802  	}
   803  }
   804  
   805  // aclPolicyTableSchema returns the MemDB schema for the policy table.
   806  // This table is used to store the policies which are referenced by tokens
   807  func aclPolicyTableSchema() *memdb.TableSchema {
   808  	return &memdb.TableSchema{
   809  		Name: "acl_policy",
   810  		Indexes: map[string]*memdb.IndexSchema{
   811  			"id": {
   812  				Name:         "id",
   813  				AllowMissing: false,
   814  				Unique:       true,
   815  				Indexer: &memdb.StringFieldIndex{
   816  					Field: "Name",
   817  				},
   818  			},
   819  			"job": {
   820  				Name:         "job",
   821  				AllowMissing: true,
   822  				Unique:       false,
   823  				Indexer:      &ACLPolicyJobACLFieldIndex{},
   824  			},
   825  		},
   826  	}
   827  }
   828  
   829  // ACLPolicyJobACLFieldIndex is used to extract the policy's JobACL field and
   830  // build an index on it.
   831  type ACLPolicyJobACLFieldIndex struct{}
   832  
   833  // FromObject is used to extract an index value from an
   834  // object or to indicate that the index value is missing.
   835  func (a *ACLPolicyJobACLFieldIndex) FromObject(obj interface{}) (bool, []byte, error) {
   836  	policy, ok := obj.(*structs.ACLPolicy)
   837  	if !ok {
   838  		return false, nil, fmt.Errorf("object %#v is not an ACLPolicy", obj)
   839  	}
   840  
   841  	if policy.JobACL == nil {
   842  		return false, nil, nil
   843  	}
   844  
   845  	ns := policy.JobACL.Namespace
   846  	if ns == "" {
   847  		return false, nil, nil
   848  	}
   849  	jobID := policy.JobACL.JobID
   850  	if jobID == "" {
   851  		return false, nil, fmt.Errorf(
   852  			"object %#v is not a valid ACLPolicy: JobACL.JobID without Namespace", obj)
   853  	}
   854  
   855  	val := ns + "\x00" + jobID + "\x00"
   856  	return true, []byte(val), nil
   857  }
   858  
   859  // FromArgs is used to build an exact index lookup based on arguments
   860  func (a *ACLPolicyJobACLFieldIndex) FromArgs(args ...interface{}) ([]byte, error) {
   861  	if len(args) != 2 {
   862  		return nil, fmt.Errorf("must provide two arguments")
   863  	}
   864  	arg0, ok := args[0].(string)
   865  	if !ok {
   866  		return nil, fmt.Errorf("argument must be a string: %#v", args[0])
   867  	}
   868  	arg1, ok := args[1].(string)
   869  	if !ok {
   870  		return nil, fmt.Errorf("argument must be a string: %#v", args[0])
   871  	}
   872  
   873  	// Add the null character as a terminator
   874  	arg0 += "\x00" + arg1 + "\x00"
   875  	return []byte(arg0), nil
   876  }
   877  
   878  // PrefixFromArgs returns a prefix that should be used for scanning based on the arguments
   879  func (a *ACLPolicyJobACLFieldIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) {
   880  	val, err := a.FromArgs(args...)
   881  	if err != nil {
   882  		return nil, err
   883  	}
   884  
   885  	// Strip the null terminator, the rest is a prefix
   886  	n := len(val)
   887  	if n > 0 {
   888  		return val[:n-1], nil
   889  	}
   890  	return val, nil
   891  }
   892  
   893  // aclTokenTableSchema returns the MemDB schema for the tokens table.
   894  // This table is used to store the bearer tokens which are used to authenticate
   895  func aclTokenTableSchema() *memdb.TableSchema {
   896  	return &memdb.TableSchema{
   897  		Name: "acl_token",
   898  		Indexes: map[string]*memdb.IndexSchema{
   899  			"id": {
   900  				Name:         "id",
   901  				AllowMissing: false,
   902  				Unique:       true,
   903  				Indexer: &memdb.UUIDFieldIndex{
   904  					Field: "AccessorID",
   905  				},
   906  			},
   907  			"create": {
   908  				Name:         "create",
   909  				AllowMissing: false,
   910  				Unique:       true,
   911  				Indexer: &memdb.CompoundIndex{
   912  					Indexes: []memdb.Indexer{
   913  						&memdb.UintFieldIndex{
   914  							Field: "CreateIndex",
   915  						},
   916  						&memdb.StringFieldIndex{
   917  							Field: "AccessorID",
   918  						},
   919  					},
   920  				},
   921  			},
   922  			"secret": {
   923  				Name:         "secret",
   924  				AllowMissing: false,
   925  				Unique:       true,
   926  				Indexer: &memdb.UUIDFieldIndex{
   927  					Field: "SecretID",
   928  				},
   929  			},
   930  			"global": {
   931  				Name:         "global",
   932  				AllowMissing: false,
   933  				Unique:       false,
   934  				Indexer: &memdb.FieldSetIndex{
   935  					Field: "Global",
   936  				},
   937  			},
   938  			indexExpiresGlobal: {
   939  				Name:         indexExpiresGlobal,
   940  				AllowMissing: true,
   941  				Unique:       false,
   942  				Indexer: indexer.SingleIndexer{
   943  					ReadIndex:  indexer.ReadIndex(indexer.IndexFromTimeQuery),
   944  					WriteIndex: indexer.WriteIndex(indexExpiresGlobalFromACLToken),
   945  				},
   946  			},
   947  			indexExpiresLocal: {
   948  				Name:         indexExpiresLocal,
   949  				AllowMissing: true,
   950  				Unique:       false,
   951  				Indexer: indexer.SingleIndexer{
   952  					ReadIndex:  indexer.ReadIndex(indexer.IndexFromTimeQuery),
   953  					WriteIndex: indexer.WriteIndex(indexExpiresLocalFromACLToken),
   954  				},
   955  			},
   956  		},
   957  	}
   958  }
   959  
   960  func indexExpiresLocalFromACLToken(raw interface{}) ([]byte, error) {
   961  	return indexExpiresFromACLToken(raw, false)
   962  }
   963  
   964  func indexExpiresGlobalFromACLToken(raw interface{}) ([]byte, error) {
   965  	return indexExpiresFromACLToken(raw, true)
   966  }
   967  
   968  // indexExpiresFromACLToken implements the indexer.WriteIndex interface and
   969  // allows us to use an ACL tokens ExpirationTime as an index, if it is a
   970  // non-default value. This allows for efficient lookups when trying to deal
   971  // with removal of expired tokens from state.
   972  func indexExpiresFromACLToken(raw interface{}, global bool) ([]byte, error) {
   973  	p, ok := raw.(*structs.ACLToken)
   974  	if !ok {
   975  		return nil, fmt.Errorf("unexpected type %T for structs.ACLToken index", raw)
   976  	}
   977  	if p.Global != global {
   978  		return nil, indexer.ErrMissingValueForIndex
   979  	}
   980  	if !p.HasExpirationTime() {
   981  		return nil, indexer.ErrMissingValueForIndex
   982  	}
   983  	if p.ExpirationTime.Unix() < 0 {
   984  		return nil, fmt.Errorf("token expiration time cannot be before the unix epoch: %s", p.ExpirationTime)
   985  	}
   986  
   987  	var b indexer.IndexBuilder
   988  	b.Time(*p.ExpirationTime)
   989  	return b.Bytes(), nil
   990  }
   991  
   992  // oneTimeTokenTableSchema returns the MemDB schema for the tokens table.
   993  // This table is used to store one-time tokens for ACL tokens
   994  func oneTimeTokenTableSchema() *memdb.TableSchema {
   995  	return &memdb.TableSchema{
   996  		Name: "one_time_token",
   997  		Indexes: map[string]*memdb.IndexSchema{
   998  			"secret": {
   999  				Name:         "secret",
  1000  				AllowMissing: false,
  1001  				Unique:       true,
  1002  				Indexer: &memdb.UUIDFieldIndex{
  1003  					Field: "OneTimeSecretID",
  1004  				},
  1005  			},
  1006  			"id": {
  1007  				Name:         "id",
  1008  				AllowMissing: false,
  1009  				Unique:       true,
  1010  				Indexer: &memdb.UUIDFieldIndex{
  1011  					Field: "AccessorID",
  1012  				},
  1013  			},
  1014  		},
  1015  	}
  1016  }
  1017  
  1018  // singletonRecord can be used to describe tables which should contain only 1 entry.
  1019  // Example uses include storing node config or cluster metadata blobs.
  1020  var singletonRecord = &memdb.ConditionalIndex{
  1021  	Conditional: func(interface{}) (bool, error) { return true, nil },
  1022  }
  1023  
  1024  // schedulerConfigTableSchema returns the MemDB schema for the scheduler config table.
  1025  // This table is used to store configuration options for the scheduler
  1026  func schedulerConfigTableSchema() *memdb.TableSchema {
  1027  	return &memdb.TableSchema{
  1028  		Name: "scheduler_config",
  1029  		Indexes: map[string]*memdb.IndexSchema{
  1030  			"id": {
  1031  				Name:         "id",
  1032  				AllowMissing: true,
  1033  				Unique:       true,
  1034  				Indexer:      singletonRecord, // we store only 1 scheduler config
  1035  			},
  1036  		},
  1037  	}
  1038  }
  1039  
  1040  // clusterMetaTableSchema returns the MemDB schema for the scheduler config table.
  1041  func clusterMetaTableSchema() *memdb.TableSchema {
  1042  	return &memdb.TableSchema{
  1043  		Name: "cluster_meta",
  1044  		Indexes: map[string]*memdb.IndexSchema{
  1045  			"id": {
  1046  				Name:         "id",
  1047  				AllowMissing: false,
  1048  				Unique:       true,
  1049  				Indexer:      singletonRecord, // we store only 1 cluster metadata
  1050  			},
  1051  		},
  1052  	}
  1053  }
  1054  
  1055  // CSIVolumes are identified by id globally, and searchable by driver
  1056  func csiVolumeTableSchema() *memdb.TableSchema {
  1057  	return &memdb.TableSchema{
  1058  		Name: "csi_volumes",
  1059  		Indexes: map[string]*memdb.IndexSchema{
  1060  			"id": {
  1061  				Name:         "id",
  1062  				AllowMissing: false,
  1063  				Unique:       true,
  1064  				Indexer: &memdb.CompoundIndex{
  1065  					Indexes: []memdb.Indexer{
  1066  						&memdb.StringFieldIndex{
  1067  							Field: "Namespace",
  1068  						},
  1069  						&memdb.StringFieldIndex{
  1070  							Field: "ID",
  1071  						},
  1072  					},
  1073  				},
  1074  			},
  1075  			"plugin_id": {
  1076  				Name:         "plugin_id",
  1077  				AllowMissing: false,
  1078  				Unique:       false,
  1079  				Indexer: &memdb.StringFieldIndex{
  1080  					Field: "PluginID",
  1081  				},
  1082  			},
  1083  		},
  1084  	}
  1085  }
  1086  
  1087  // CSIPlugins are identified by id globally, and searchable by driver
  1088  func csiPluginTableSchema() *memdb.TableSchema {
  1089  	return &memdb.TableSchema{
  1090  		Name: "csi_plugins",
  1091  		Indexes: map[string]*memdb.IndexSchema{
  1092  			"id": {
  1093  				Name:         "id",
  1094  				AllowMissing: false,
  1095  				Unique:       true,
  1096  				Indexer: &memdb.StringFieldIndex{
  1097  					Field: "ID",
  1098  				},
  1099  			},
  1100  		},
  1101  	}
  1102  }
  1103  
  1104  // StringFieldIndex is used to extract a field from an object
  1105  // using reflection and builds an index on that field.
  1106  type ScalingPolicyTargetFieldIndex struct {
  1107  	Field string
  1108  
  1109  	// AllowMissing controls if the field should be ignored if the field is
  1110  	// not provided.
  1111  	AllowMissing bool
  1112  }
  1113  
  1114  // FromObject is used to extract an index value from an
  1115  // object or to indicate that the index value is missing.
  1116  func (s *ScalingPolicyTargetFieldIndex) FromObject(obj interface{}) (bool, []byte, error) {
  1117  	policy, ok := obj.(*structs.ScalingPolicy)
  1118  	if !ok {
  1119  		return false, nil, fmt.Errorf("object %#v is not a ScalingPolicy", obj)
  1120  	}
  1121  
  1122  	if policy.Target == nil {
  1123  		return false, nil, nil
  1124  	}
  1125  
  1126  	val, ok := policy.Target[s.Field]
  1127  	if !ok && !s.AllowMissing {
  1128  		return false, nil, nil
  1129  	}
  1130  
  1131  	// Add the null character as a terminator
  1132  	val += "\x00"
  1133  	return true, []byte(val), nil
  1134  }
  1135  
  1136  // FromArgs is used to build an exact index lookup based on arguments
  1137  func (s *ScalingPolicyTargetFieldIndex) FromArgs(args ...interface{}) ([]byte, error) {
  1138  	if len(args) != 1 {
  1139  		return nil, fmt.Errorf("must provide only a single argument")
  1140  	}
  1141  	arg, ok := args[0].(string)
  1142  	if !ok {
  1143  		return nil, fmt.Errorf("argument must be a string: %#v", args[0])
  1144  	}
  1145  	// Add the null character as a terminator
  1146  	arg += "\x00"
  1147  	return []byte(arg), nil
  1148  }
  1149  
  1150  // PrefixFromArgs returns a prefix that should be used for scanning based on the arguments
  1151  func (s *ScalingPolicyTargetFieldIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) {
  1152  	val, err := s.FromArgs(args...)
  1153  	if err != nil {
  1154  		return nil, err
  1155  	}
  1156  
  1157  	// Strip the null terminator, the rest is a prefix
  1158  	n := len(val)
  1159  	if n > 0 {
  1160  		return val[:n-1], nil
  1161  	}
  1162  	return val, nil
  1163  }
  1164  
  1165  // scalingPolicyTableSchema returns the MemDB schema for the policy table.
  1166  func scalingPolicyTableSchema() *memdb.TableSchema {
  1167  	return &memdb.TableSchema{
  1168  		Name: "scaling_policy",
  1169  		Indexes: map[string]*memdb.IndexSchema{
  1170  			// Primary index is used for simple direct lookup.
  1171  			"id": {
  1172  				Name:         "id",
  1173  				AllowMissing: false,
  1174  				Unique:       true,
  1175  
  1176  				// UUID is uniquely identifying
  1177  				Indexer: &memdb.StringFieldIndex{
  1178  					Field: "ID",
  1179  				},
  1180  			},
  1181  			// Target index is used for listing by namespace or job, or looking up a specific target.
  1182  			// A given task group can have only a single scaling policies, so this is guaranteed to be unique.
  1183  			"target": {
  1184  				Name:   "target",
  1185  				Unique: false,
  1186  
  1187  				// Use a compound index so the tuple of (Namespace, Job, Group, Task) is
  1188  				// used when looking for a policy
  1189  				Indexer: &memdb.CompoundIndex{
  1190  					Indexes: []memdb.Indexer{
  1191  						&ScalingPolicyTargetFieldIndex{
  1192  							Field:        "Namespace",
  1193  							AllowMissing: true,
  1194  						},
  1195  
  1196  						&ScalingPolicyTargetFieldIndex{
  1197  							Field:        "Job",
  1198  							AllowMissing: true,
  1199  						},
  1200  
  1201  						&ScalingPolicyTargetFieldIndex{
  1202  							Field:        "Group",
  1203  							AllowMissing: true,
  1204  						},
  1205  
  1206  						&ScalingPolicyTargetFieldIndex{
  1207  							Field:        "Task",
  1208  							AllowMissing: true,
  1209  						},
  1210  					},
  1211  				},
  1212  			},
  1213  			// Type index is used for listing by policy type
  1214  			"type": {
  1215  				Name:         "type",
  1216  				AllowMissing: false,
  1217  				Unique:       false,
  1218  				Indexer: &memdb.StringFieldIndex{
  1219  					Field: "Type",
  1220  				},
  1221  			},
  1222  			// Used to filter by enabled
  1223  			"enabled": {
  1224  				Name:         "enabled",
  1225  				AllowMissing: false,
  1226  				Unique:       false,
  1227  				Indexer: &memdb.FieldSetIndex{
  1228  					Field: "Enabled",
  1229  				},
  1230  			},
  1231  		},
  1232  	}
  1233  }
  1234  
  1235  // scalingEventTableSchema returns the memdb schema for job scaling events
  1236  func scalingEventTableSchema() *memdb.TableSchema {
  1237  	return &memdb.TableSchema{
  1238  		Name: "scaling_event",
  1239  		Indexes: map[string]*memdb.IndexSchema{
  1240  			"id": {
  1241  				Name:         "id",
  1242  				AllowMissing: false,
  1243  				Unique:       true,
  1244  
  1245  				// Use a compound index so the tuple of (Namespace, JobID) is
  1246  				// uniquely identifying
  1247  				Indexer: &memdb.CompoundIndex{
  1248  					Indexes: []memdb.Indexer{
  1249  						&memdb.StringFieldIndex{
  1250  							Field: "Namespace",
  1251  						},
  1252  
  1253  						&memdb.StringFieldIndex{
  1254  							Field: "JobID",
  1255  						},
  1256  					},
  1257  				},
  1258  			},
  1259  
  1260  			// TODO: need to figure out whether we want to index these or the jobs or ...
  1261  			// "error": {
  1262  			// 	Name:         "error",
  1263  			// 	AllowMissing: false,
  1264  			// 	Unique:       false,
  1265  			// 	Indexer: &memdb.FieldSetIndex{
  1266  			// 		Field: "Error",
  1267  			// 	},
  1268  			// },
  1269  		},
  1270  	}
  1271  }
  1272  
  1273  // namespaceTableSchema returns the MemDB schema for the namespace table.
  1274  func namespaceTableSchema() *memdb.TableSchema {
  1275  	return &memdb.TableSchema{
  1276  		Name: TableNamespaces,
  1277  		Indexes: map[string]*memdb.IndexSchema{
  1278  			"id": {
  1279  				Name:         "id",
  1280  				AllowMissing: false,
  1281  				Unique:       true,
  1282  				Indexer: &memdb.StringFieldIndex{
  1283  					Field: "Name",
  1284  				},
  1285  			},
  1286  			"quota": {
  1287  				Name:         "quota",
  1288  				AllowMissing: true,
  1289  				Unique:       false,
  1290  				Indexer: &memdb.StringFieldIndex{
  1291  					Field: "Quota",
  1292  				},
  1293  			},
  1294  		},
  1295  	}
  1296  }
  1297  
  1298  // serviceRegistrationsTableSchema returns the MemDB schema for Nomad native
  1299  // service registrations.
  1300  func serviceRegistrationsTableSchema() *memdb.TableSchema {
  1301  	return &memdb.TableSchema{
  1302  		Name: TableServiceRegistrations,
  1303  		Indexes: map[string]*memdb.IndexSchema{
  1304  			// The serviceID in combination with namespace forms a unique
  1305  			// identifier for a service registration. This is used to look up
  1306  			// and delete services in individual isolation.
  1307  			indexID: {
  1308  				Name:         indexID,
  1309  				AllowMissing: false,
  1310  				Unique:       true,
  1311  				Indexer: &memdb.CompoundIndex{
  1312  					Indexes: []memdb.Indexer{
  1313  						&memdb.StringFieldIndex{
  1314  							Field: "Namespace",
  1315  						},
  1316  						&memdb.StringFieldIndex{
  1317  							Field: "ID",
  1318  						},
  1319  					},
  1320  				},
  1321  			},
  1322  			indexServiceName: {
  1323  				Name:         indexServiceName,
  1324  				AllowMissing: false,
  1325  				Unique:       false,
  1326  				Indexer: &memdb.CompoundIndex{
  1327  					Indexes: []memdb.Indexer{
  1328  						&memdb.StringFieldIndex{
  1329  							Field: "Namespace",
  1330  						},
  1331  						&memdb.StringFieldIndex{
  1332  							Field: "ServiceName",
  1333  						},
  1334  					},
  1335  				},
  1336  			},
  1337  			indexJob: {
  1338  				Name:         indexJob,
  1339  				AllowMissing: false,
  1340  				Unique:       false,
  1341  				Indexer: &memdb.CompoundIndex{
  1342  					Indexes: []memdb.Indexer{
  1343  						&memdb.StringFieldIndex{
  1344  							Field: "Namespace",
  1345  						},
  1346  						&memdb.StringFieldIndex{
  1347  							Field: "JobID",
  1348  						},
  1349  					},
  1350  				},
  1351  			},
  1352  			// The nodeID index allows lookups and deletions to be performed
  1353  			// for an entire node. This is primarily used when a node becomes
  1354  			// lost.
  1355  			indexNodeID: {
  1356  				Name:         indexNodeID,
  1357  				AllowMissing: false,
  1358  				Unique:       false,
  1359  				Indexer: &memdb.StringFieldIndex{
  1360  					Field: "NodeID",
  1361  				},
  1362  			},
  1363  			indexAllocID: {
  1364  				Name:         indexAllocID,
  1365  				AllowMissing: false,
  1366  				Unique:       false,
  1367  				Indexer: &memdb.StringFieldIndex{
  1368  					Field: "AllocID",
  1369  				},
  1370  			},
  1371  		},
  1372  	}
  1373  }
  1374  
  1375  // variablesTableSchema returns the MemDB schema for Nomad variables.
  1376  func variablesTableSchema() *memdb.TableSchema {
  1377  	return &memdb.TableSchema{
  1378  		Name: TableVariables,
  1379  		Indexes: map[string]*memdb.IndexSchema{
  1380  			indexID: {
  1381  				Name:         indexID,
  1382  				AllowMissing: false,
  1383  				Unique:       true,
  1384  				Indexer: &memdb.CompoundIndex{
  1385  					Indexes: []memdb.Indexer{
  1386  						&memdb.StringFieldIndex{
  1387  							Field: "Namespace",
  1388  						},
  1389  						&memdb.StringFieldIndex{
  1390  							Field: "Path",
  1391  						},
  1392  					},
  1393  				},
  1394  			},
  1395  			indexKeyID: {
  1396  				Name:         indexKeyID,
  1397  				AllowMissing: false,
  1398  				Indexer:      &variableKeyIDFieldIndexer{},
  1399  			},
  1400  			indexPath: {
  1401  				Name:         indexPath,
  1402  				AllowMissing: false,
  1403  				Unique:       false,
  1404  				Indexer: &memdb.StringFieldIndex{
  1405  					Field: "Path",
  1406  				},
  1407  			},
  1408  		},
  1409  	}
  1410  }
  1411  
  1412  type variableKeyIDFieldIndexer struct{}
  1413  
  1414  // FromArgs implements go-memdb/Indexer and is used to build an exact
  1415  // index lookup based on arguments
  1416  func (s *variableKeyIDFieldIndexer) FromArgs(args ...interface{}) ([]byte, error) {
  1417  	if len(args) != 1 {
  1418  		return nil, fmt.Errorf("must provide only a single argument")
  1419  	}
  1420  	arg, ok := args[0].(string)
  1421  	if !ok {
  1422  		return nil, fmt.Errorf("argument must be a string: %#v", args[0])
  1423  	}
  1424  	// Add the null character as a terminator
  1425  	arg += "\x00"
  1426  	return []byte(arg), nil
  1427  }
  1428  
  1429  // PrefixFromArgs implements go-memdb/PrefixIndexer and returns a
  1430  // prefix that should be used for scanning based on the arguments
  1431  func (s *variableKeyIDFieldIndexer) PrefixFromArgs(args ...interface{}) ([]byte, error) {
  1432  	val, err := s.FromArgs(args...)
  1433  	if err != nil {
  1434  		return nil, err
  1435  	}
  1436  
  1437  	// Strip the null terminator, the rest is a prefix
  1438  	n := len(val)
  1439  	if n > 0 {
  1440  		return val[:n-1], nil
  1441  	}
  1442  	return val, nil
  1443  }
  1444  
  1445  // FromObject implements go-memdb/SingleIndexer and is used to extract
  1446  // an index value from an object or to indicate that the index value
  1447  // is missing.
  1448  func (s *variableKeyIDFieldIndexer) FromObject(obj interface{}) (bool, []byte, error) {
  1449  	variable, ok := obj.(*structs.VariableEncrypted)
  1450  	if !ok {
  1451  		return false, nil, fmt.Errorf("object %#v is not a Variable", obj)
  1452  	}
  1453  
  1454  	keyID := variable.KeyID
  1455  	if keyID == "" {
  1456  		return false, nil, nil
  1457  	}
  1458  
  1459  	// Add the null character as a terminator
  1460  	keyID += "\x00"
  1461  	return true, []byte(keyID), nil
  1462  }
  1463  
  1464  // variablesQuotasTableSchema returns the MemDB schema for Nomad variables
  1465  // quotas tracking
  1466  func variablesQuotasTableSchema() *memdb.TableSchema {
  1467  	return &memdb.TableSchema{
  1468  		Name: TableVariablesQuotas,
  1469  		Indexes: map[string]*memdb.IndexSchema{
  1470  			indexID: {
  1471  				Name:         indexID,
  1472  				AllowMissing: false,
  1473  				Unique:       true,
  1474  				Indexer: &memdb.StringFieldIndex{
  1475  					Field:     "Namespace",
  1476  					Lowercase: true,
  1477  				},
  1478  			},
  1479  		},
  1480  	}
  1481  }
  1482  
  1483  // variablesRootKeyMetaSchema returns the MemDB schema for Nomad root keys
  1484  func variablesRootKeyMetaSchema() *memdb.TableSchema {
  1485  	return &memdb.TableSchema{
  1486  		Name: TableRootKeyMeta,
  1487  		Indexes: map[string]*memdb.IndexSchema{
  1488  			indexID: {
  1489  				Name:         indexID,
  1490  				AllowMissing: false,
  1491  				Unique:       true,
  1492  				Indexer: &memdb.StringFieldIndex{
  1493  					Field:     "KeyID",
  1494  					Lowercase: true,
  1495  				},
  1496  			},
  1497  		},
  1498  	}
  1499  }
  1500  
  1501  func aclRolesTableSchema() *memdb.TableSchema {
  1502  	return &memdb.TableSchema{
  1503  		Name: TableACLRoles,
  1504  		Indexes: map[string]*memdb.IndexSchema{
  1505  			indexID: {
  1506  				Name:         indexID,
  1507  				AllowMissing: false,
  1508  				Unique:       true,
  1509  				Indexer: &memdb.StringFieldIndex{
  1510  					Field: "ID",
  1511  				},
  1512  			},
  1513  			indexName: {
  1514  				Name:         indexName,
  1515  				AllowMissing: false,
  1516  				Unique:       true,
  1517  				Indexer: &memdb.StringFieldIndex{
  1518  					Field: "Name",
  1519  				},
  1520  			},
  1521  		},
  1522  	}
  1523  }
  1524  
  1525  func aclAuthMethodsTableSchema() *memdb.TableSchema {
  1526  	return &memdb.TableSchema{
  1527  		Name: TableACLAuthMethods,
  1528  		Indexes: map[string]*memdb.IndexSchema{
  1529  			indexID: {
  1530  				Name:         indexID,
  1531  				AllowMissing: false,
  1532  				Unique:       true,
  1533  				Indexer: &memdb.StringFieldIndex{
  1534  					Field: "Name",
  1535  				},
  1536  			},
  1537  		},
  1538  	}
  1539  }
  1540  
  1541  func bindingRulesTableSchema() *memdb.TableSchema {
  1542  	return &memdb.TableSchema{
  1543  		Name: TableACLBindingRules,
  1544  		Indexes: map[string]*memdb.IndexSchema{
  1545  			indexID: {
  1546  				Name:         indexID,
  1547  				AllowMissing: false,
  1548  				Unique:       true,
  1549  				Indexer: &memdb.StringFieldIndex{
  1550  					Field: "ID",
  1551  				},
  1552  			},
  1553  			indexAuthMethod: {
  1554  				Name:         indexAuthMethod,
  1555  				AllowMissing: false,
  1556  				Unique:       false,
  1557  				Indexer: &memdb.StringFieldIndex{
  1558  					Field: "AuthMethod",
  1559  				},
  1560  			},
  1561  		},
  1562  	}
  1563  }